162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/ethtool.h> 562306a36Sopenharmony_ci#include <linux/printk.h> 662306a36Sopenharmony_ci#include <linux/dynamic_debug.h> 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/if_vlan.h> 1062306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/cpumask.h> 1462306a36Sopenharmony_ci#include <linux/crash_dump.h> 1562306a36Sopenharmony_ci#include <linux/vmalloc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "ionic.h" 1862306a36Sopenharmony_ci#include "ionic_bus.h" 1962306a36Sopenharmony_ci#include "ionic_dev.h" 2062306a36Sopenharmony_ci#include "ionic_lif.h" 2162306a36Sopenharmony_ci#include "ionic_txrx.h" 2262306a36Sopenharmony_ci#include "ionic_ethtool.h" 2362306a36Sopenharmony_ci#include "ionic_debugfs.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* queuetype support level */ 2662306a36Sopenharmony_cistatic const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = { 2762306a36Sopenharmony_ci [IONIC_QTYPE_ADMINQ] = 0, /* 0 = Base version with CQ support */ 2862306a36Sopenharmony_ci [IONIC_QTYPE_NOTIFYQ] = 0, /* 0 = Base version */ 2962306a36Sopenharmony_ci [IONIC_QTYPE_RXQ] = 2, /* 0 = Base version with CQ+SG support 3062306a36Sopenharmony_ci * 2 = ... with CMB rings 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci [IONIC_QTYPE_TXQ] = 3, /* 0 = Base version with CQ+SG support 3362306a36Sopenharmony_ci * 1 = ... with Tx SG version 1 3462306a36Sopenharmony_ci * 3 = ... with CMB rings 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void ionic_link_status_check(struct ionic_lif *lif); 3962306a36Sopenharmony_cistatic void ionic_lif_handle_fw_down(struct ionic_lif *lif); 4062306a36Sopenharmony_cistatic void ionic_lif_handle_fw_up(struct ionic_lif *lif); 4162306a36Sopenharmony_cistatic void ionic_lif_set_netdev_info(struct ionic_lif *lif); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void ionic_txrx_deinit(struct ionic_lif *lif); 4462306a36Sopenharmony_cistatic int ionic_txrx_init(struct ionic_lif *lif); 4562306a36Sopenharmony_cistatic int ionic_start_queues(struct ionic_lif *lif); 4662306a36Sopenharmony_cistatic void ionic_stop_queues(struct ionic_lif *lif); 4762306a36Sopenharmony_cistatic void ionic_lif_queue_identify(struct ionic_lif *lif); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void ionic_dim_work(struct work_struct *work) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct dim *dim = container_of(work, struct dim, work); 5262306a36Sopenharmony_ci struct ionic_intr_info *intr; 5362306a36Sopenharmony_ci struct dim_cq_moder cur_moder; 5462306a36Sopenharmony_ci struct ionic_qcq *qcq; 5562306a36Sopenharmony_ci struct ionic_lif *lif; 5662306a36Sopenharmony_ci u32 new_coal; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); 5962306a36Sopenharmony_ci qcq = container_of(dim, struct ionic_qcq, dim); 6062306a36Sopenharmony_ci lif = qcq->q.lif; 6162306a36Sopenharmony_ci new_coal = ionic_coal_usec_to_hw(lif->ionic, cur_moder.usec); 6262306a36Sopenharmony_ci new_coal = new_coal ? new_coal : 1; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci intr = &qcq->intr; 6562306a36Sopenharmony_ci if (intr->dim_coal_hw != new_coal) { 6662306a36Sopenharmony_ci intr->dim_coal_hw = new_coal; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 6962306a36Sopenharmony_ci intr->index, intr->dim_coal_hw); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci dim->state = DIM_START_MEASURE; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void ionic_lif_deferred_work(struct work_struct *work) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct ionic_lif *lif = container_of(work, struct ionic_lif, deferred.work); 7862306a36Sopenharmony_ci struct ionic_deferred *def = &lif->deferred; 7962306a36Sopenharmony_ci struct ionic_deferred_work *w = NULL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci do { 8262306a36Sopenharmony_ci spin_lock_bh(&def->lock); 8362306a36Sopenharmony_ci if (!list_empty(&def->list)) { 8462306a36Sopenharmony_ci w = list_first_entry(&def->list, 8562306a36Sopenharmony_ci struct ionic_deferred_work, list); 8662306a36Sopenharmony_ci list_del(&w->list); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci spin_unlock_bh(&def->lock); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!w) 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci switch (w->type) { 9462306a36Sopenharmony_ci case IONIC_DW_TYPE_RX_MODE: 9562306a36Sopenharmony_ci ionic_lif_rx_mode(lif); 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case IONIC_DW_TYPE_LINK_STATUS: 9862306a36Sopenharmony_ci ionic_link_status_check(lif); 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case IONIC_DW_TYPE_LIF_RESET: 10162306a36Sopenharmony_ci if (w->fw_status) { 10262306a36Sopenharmony_ci ionic_lif_handle_fw_up(lif); 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci ionic_lif_handle_fw_down(lif); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Fire off another watchdog to see 10762306a36Sopenharmony_ci * if the FW is already back rather than 10862306a36Sopenharmony_ci * waiting another whole cycle 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci mod_timer(&lif->ionic->watchdog_timer, jiffies + 1); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci default: 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci kfree(w); 11762306a36Sopenharmony_ci w = NULL; 11862306a36Sopenharmony_ci } while (true); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid ionic_lif_deferred_enqueue(struct ionic_deferred *def, 12262306a36Sopenharmony_ci struct ionic_deferred_work *work) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci spin_lock_bh(&def->lock); 12562306a36Sopenharmony_ci list_add_tail(&work->list, &def->list); 12662306a36Sopenharmony_ci spin_unlock_bh(&def->lock); 12762306a36Sopenharmony_ci schedule_work(&def->work); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void ionic_link_status_check(struct ionic_lif *lif) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct net_device *netdev = lif->netdev; 13362306a36Sopenharmony_ci u16 link_status; 13462306a36Sopenharmony_ci bool link_up; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state)) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Don't put carrier back up if we're in a broken state */ 14062306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_BROKEN, lif->state)) { 14162306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state); 14262306a36Sopenharmony_ci return; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci link_status = le16_to_cpu(lif->info->status.link_status); 14662306a36Sopenharmony_ci link_up = link_status == IONIC_PORT_OPER_STATUS_UP; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (link_up) { 14962306a36Sopenharmony_ci int err = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (netdev->flags & IFF_UP && netif_running(netdev)) { 15262306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 15362306a36Sopenharmony_ci err = ionic_start_queues(lif); 15462306a36Sopenharmony_ci if (err && err != -EBUSY) { 15562306a36Sopenharmony_ci netdev_err(netdev, 15662306a36Sopenharmony_ci "Failed to start queues: %d\n", err); 15762306a36Sopenharmony_ci set_bit(IONIC_LIF_F_BROKEN, lif->state); 15862306a36Sopenharmony_ci netif_carrier_off(lif->netdev); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!err && !netif_carrier_ok(netdev)) { 16462306a36Sopenharmony_ci ionic_port_identify(lif->ionic); 16562306a36Sopenharmony_ci netdev_info(netdev, "Link up - %d Gbps\n", 16662306a36Sopenharmony_ci le32_to_cpu(lif->info->status.link_speed) / 1000); 16762306a36Sopenharmony_ci netif_carrier_on(netdev); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci if (netif_carrier_ok(netdev)) { 17162306a36Sopenharmony_ci lif->link_down_count++; 17262306a36Sopenharmony_ci netdev_info(netdev, "Link down\n"); 17362306a36Sopenharmony_ci netif_carrier_off(netdev); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (netdev->flags & IFF_UP && netif_running(netdev)) { 17762306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 17862306a36Sopenharmony_ci ionic_stop_queues(lif); 17962306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_civoid ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct ionic_deferred_work *work; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* we only need one request outstanding at a time */ 19162306a36Sopenharmony_ci if (test_and_set_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state)) 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!can_sleep) { 19562306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_ATOMIC); 19662306a36Sopenharmony_ci if (!work) { 19762306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state); 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci work->type = IONIC_DW_TYPE_LINK_STATUS; 20262306a36Sopenharmony_ci ionic_lif_deferred_enqueue(&lif->deferred, work); 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci ionic_link_status_check(lif); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void ionic_napi_deadline(struct timer_list *timer) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct ionic_qcq *qcq = container_of(timer, struct ionic_qcq, napi_deadline); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci napi_schedule(&qcq->napi); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic irqreturn_t ionic_isr(int irq, void *data) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct napi_struct *napi = data; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci napi_schedule_irqoff(napi); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return IRQ_HANDLED; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int ionic_request_irq(struct ionic_lif *lif, struct ionic_qcq *qcq) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct ionic_intr_info *intr = &qcq->intr; 22762306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 22862306a36Sopenharmony_ci struct ionic_queue *q = &qcq->q; 22962306a36Sopenharmony_ci const char *name; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (lif->registered) 23262306a36Sopenharmony_ci name = lif->netdev->name; 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci name = dev_name(dev); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci snprintf(intr->name, sizeof(intr->name), 23762306a36Sopenharmony_ci "%s-%s-%s", IONIC_DRV_NAME, name, q->name); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return devm_request_irq(dev, intr->vector, ionic_isr, 24062306a36Sopenharmony_ci 0, intr->name, &qcq->napi); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 24662306a36Sopenharmony_ci int index; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci index = find_first_zero_bit(ionic->intrs, ionic->nintrs); 24962306a36Sopenharmony_ci if (index == ionic->nintrs) { 25062306a36Sopenharmony_ci netdev_warn(lif->netdev, "%s: no intr, index=%d nintrs=%d\n", 25162306a36Sopenharmony_ci __func__, index, ionic->nintrs); 25262306a36Sopenharmony_ci return -ENOSPC; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci set_bit(index, ionic->intrs); 25662306a36Sopenharmony_ci ionic_intr_init(&ionic->idev, intr, index); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void ionic_intr_free(struct ionic *ionic, int index) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci if (index != IONIC_INTR_INDEX_NOT_ASSIGNED && index < ionic->nintrs) 26462306a36Sopenharmony_ci clear_bit(index, ionic->intrs); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int ionic_qcq_enable(struct ionic_qcq *qcq) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct ionic_queue *q = &qcq->q; 27062306a36Sopenharmony_ci struct ionic_lif *lif = q->lif; 27162306a36Sopenharmony_ci struct ionic_dev *idev; 27262306a36Sopenharmony_ci struct device *dev; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 27562306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 27662306a36Sopenharmony_ci .cmd.q_control = { 27762306a36Sopenharmony_ci .opcode = IONIC_CMD_Q_CONTROL, 27862306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 27962306a36Sopenharmony_ci .type = q->type, 28062306a36Sopenharmony_ci .index = cpu_to_le32(q->index), 28162306a36Sopenharmony_ci .oper = IONIC_Q_ENABLE, 28262306a36Sopenharmony_ci }, 28362306a36Sopenharmony_ci }; 28462306a36Sopenharmony_ci int ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci idev = &lif->ionic->idev; 28762306a36Sopenharmony_ci dev = lif->ionic->dev; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev_dbg(dev, "q_enable.index %d q_enable.qtype %d\n", 29062306a36Sopenharmony_ci ctx.cmd.q_control.index, ctx.cmd.q_control.type); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_INTR) 29362306a36Sopenharmony_ci ionic_intr_clean(idev->intr_ctrl, qcq->intr.index); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = ionic_adminq_post_wait(lif, &ctx); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (qcq->napi.poll) 30062306a36Sopenharmony_ci napi_enable(&qcq->napi); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_INTR) { 30362306a36Sopenharmony_ci irq_set_affinity_hint(qcq->intr.vector, 30462306a36Sopenharmony_ci &qcq->intr.affinity_mask); 30562306a36Sopenharmony_ci ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, 30662306a36Sopenharmony_ci IONIC_INTR_MASK_CLEAR); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int ionic_qcq_disable(struct ionic_lif *lif, struct ionic_qcq *qcq, int fw_err) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct ionic_queue *q; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 31762306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 31862306a36Sopenharmony_ci .cmd.q_control = { 31962306a36Sopenharmony_ci .opcode = IONIC_CMD_Q_CONTROL, 32062306a36Sopenharmony_ci .oper = IONIC_Q_DISABLE, 32162306a36Sopenharmony_ci }, 32262306a36Sopenharmony_ci }; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!qcq) { 32562306a36Sopenharmony_ci netdev_err(lif->netdev, "%s: bad qcq\n", __func__); 32662306a36Sopenharmony_ci return -ENXIO; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci q = &qcq->q; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_INTR) { 33262306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci cancel_work_sync(&qcq->dim.work); 33562306a36Sopenharmony_ci ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, 33662306a36Sopenharmony_ci IONIC_INTR_MASK_SET); 33762306a36Sopenharmony_ci synchronize_irq(qcq->intr.vector); 33862306a36Sopenharmony_ci irq_set_affinity_hint(qcq->intr.vector, NULL); 33962306a36Sopenharmony_ci napi_disable(&qcq->napi); 34062306a36Sopenharmony_ci del_timer_sync(&qcq->napi_deadline); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* If there was a previous fw communcation error, don't bother with 34462306a36Sopenharmony_ci * sending the adminq command and just return the same error value. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci if (fw_err == -ETIMEDOUT || fw_err == -ENXIO) 34762306a36Sopenharmony_ci return fw_err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ctx.cmd.q_control.lif_index = cpu_to_le16(lif->index); 35062306a36Sopenharmony_ci ctx.cmd.q_control.type = q->type; 35162306a36Sopenharmony_ci ctx.cmd.q_control.index = cpu_to_le32(q->index); 35262306a36Sopenharmony_ci dev_dbg(lif->ionic->dev, "q_disable.index %d q_disable.qtype %d\n", 35362306a36Sopenharmony_ci ctx.cmd.q_control.index, ctx.cmd.q_control.type); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return ionic_adminq_post_wait(lif, &ctx); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!qcq) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!(qcq->flags & IONIC_QCQ_F_INITED)) 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_INTR) { 36962306a36Sopenharmony_ci ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, 37062306a36Sopenharmony_ci IONIC_INTR_MASK_SET); 37162306a36Sopenharmony_ci netif_napi_del(&qcq->napi); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci qcq->flags &= ~IONIC_QCQ_F_INITED; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void ionic_qcq_intr_free(struct ionic_lif *lif, struct ionic_qcq *qcq) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci if (!(qcq->flags & IONIC_QCQ_F_INTR) || qcq->intr.vector == 0) 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci irq_set_affinity_hint(qcq->intr.vector, NULL); 38362306a36Sopenharmony_ci devm_free_irq(lif->ionic->dev, qcq->intr.vector, &qcq->napi); 38462306a36Sopenharmony_ci qcq->intr.vector = 0; 38562306a36Sopenharmony_ci ionic_intr_free(lif->ionic, qcq->intr.index); 38662306a36Sopenharmony_ci qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!qcq) 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ionic_debugfs_del_qcq(qcq); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (qcq->q_base) { 39962306a36Sopenharmony_ci dma_free_coherent(dev, qcq->q_size, qcq->q_base, qcq->q_base_pa); 40062306a36Sopenharmony_ci qcq->q_base = NULL; 40162306a36Sopenharmony_ci qcq->q_base_pa = 0; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (qcq->cmb_q_base) { 40562306a36Sopenharmony_ci iounmap(qcq->cmb_q_base); 40662306a36Sopenharmony_ci ionic_put_cmb(lif, qcq->cmb_pgid, qcq->cmb_order); 40762306a36Sopenharmony_ci qcq->cmb_pgid = 0; 40862306a36Sopenharmony_ci qcq->cmb_order = 0; 40962306a36Sopenharmony_ci qcq->cmb_q_base = NULL; 41062306a36Sopenharmony_ci qcq->cmb_q_base_pa = 0; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (qcq->cq_base) { 41462306a36Sopenharmony_ci dma_free_coherent(dev, qcq->cq_size, qcq->cq_base, qcq->cq_base_pa); 41562306a36Sopenharmony_ci qcq->cq_base = NULL; 41662306a36Sopenharmony_ci qcq->cq_base_pa = 0; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (qcq->sg_base) { 42062306a36Sopenharmony_ci dma_free_coherent(dev, qcq->sg_size, qcq->sg_base, qcq->sg_base_pa); 42162306a36Sopenharmony_ci qcq->sg_base = NULL; 42262306a36Sopenharmony_ci qcq->sg_base_pa = 0; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ionic_qcq_intr_free(lif, qcq); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (qcq->cq.info) { 42862306a36Sopenharmony_ci vfree(qcq->cq.info); 42962306a36Sopenharmony_ci qcq->cq.info = NULL; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci if (qcq->q.info) { 43262306a36Sopenharmony_ci vfree(qcq->q.info); 43362306a36Sopenharmony_ci qcq->q.info = NULL; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_civoid ionic_qcqs_free(struct ionic_lif *lif) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 44062306a36Sopenharmony_ci struct ionic_qcq *adminqcq; 44162306a36Sopenharmony_ci unsigned long irqflags; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (lif->notifyqcq) { 44462306a36Sopenharmony_ci ionic_qcq_free(lif, lif->notifyqcq); 44562306a36Sopenharmony_ci devm_kfree(dev, lif->notifyqcq); 44662306a36Sopenharmony_ci lif->notifyqcq = NULL; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (lif->adminqcq) { 45062306a36Sopenharmony_ci spin_lock_irqsave(&lif->adminq_lock, irqflags); 45162306a36Sopenharmony_ci adminqcq = READ_ONCE(lif->adminqcq); 45262306a36Sopenharmony_ci lif->adminqcq = NULL; 45362306a36Sopenharmony_ci spin_unlock_irqrestore(&lif->adminq_lock, irqflags); 45462306a36Sopenharmony_ci if (adminqcq) { 45562306a36Sopenharmony_ci ionic_qcq_free(lif, adminqcq); 45662306a36Sopenharmony_ci devm_kfree(dev, adminqcq); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (lif->rxqcqs) { 46162306a36Sopenharmony_ci devm_kfree(dev, lif->rxqstats); 46262306a36Sopenharmony_ci lif->rxqstats = NULL; 46362306a36Sopenharmony_ci devm_kfree(dev, lif->rxqcqs); 46462306a36Sopenharmony_ci lif->rxqcqs = NULL; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (lif->txqcqs) { 46862306a36Sopenharmony_ci devm_kfree(dev, lif->txqstats); 46962306a36Sopenharmony_ci lif->txqstats = NULL; 47062306a36Sopenharmony_ci devm_kfree(dev, lif->txqcqs); 47162306a36Sopenharmony_ci lif->txqcqs = NULL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq, 47662306a36Sopenharmony_ci struct ionic_qcq *n_qcq) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci n_qcq->intr.vector = src_qcq->intr.vector; 47962306a36Sopenharmony_ci n_qcq->intr.index = src_qcq->intr.index; 48062306a36Sopenharmony_ci n_qcq->napi_qcq = src_qcq->napi_qcq; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qcq) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int err; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!(qcq->flags & IONIC_QCQ_F_INTR)) { 48862306a36Sopenharmony_ci qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED; 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci err = ionic_intr_alloc(lif, &qcq->intr); 49362306a36Sopenharmony_ci if (err) { 49462306a36Sopenharmony_ci netdev_warn(lif->netdev, "no intr for %s: %d\n", 49562306a36Sopenharmony_ci qcq->q.name, err); 49662306a36Sopenharmony_ci goto err_out; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci err = ionic_bus_get_irq(lif->ionic, qcq->intr.index); 50062306a36Sopenharmony_ci if (err < 0) { 50162306a36Sopenharmony_ci netdev_warn(lif->netdev, "no vector for %s: %d\n", 50262306a36Sopenharmony_ci qcq->q.name, err); 50362306a36Sopenharmony_ci goto err_out_free_intr; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci qcq->intr.vector = err; 50662306a36Sopenharmony_ci ionic_intr_mask_assert(lif->ionic->idev.intr_ctrl, qcq->intr.index, 50762306a36Sopenharmony_ci IONIC_INTR_MASK_SET); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci err = ionic_request_irq(lif, qcq); 51062306a36Sopenharmony_ci if (err) { 51162306a36Sopenharmony_ci netdev_warn(lif->netdev, "irq request failed %d\n", err); 51262306a36Sopenharmony_ci goto err_out_free_intr; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* try to get the irq on the local numa node first */ 51662306a36Sopenharmony_ci qcq->intr.cpu = cpumask_local_spread(qcq->intr.index, 51762306a36Sopenharmony_ci dev_to_node(lif->ionic->dev)); 51862306a36Sopenharmony_ci if (qcq->intr.cpu != -1) 51962306a36Sopenharmony_ci cpumask_set_cpu(qcq->intr.cpu, &qcq->intr.affinity_mask); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci netdev_dbg(lif->netdev, "%s: Interrupt index %d\n", qcq->q.name, qcq->intr.index); 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cierr_out_free_intr: 52562306a36Sopenharmony_ci ionic_intr_free(lif->ionic, qcq->intr.index); 52662306a36Sopenharmony_cierr_out: 52762306a36Sopenharmony_ci return err; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, 53162306a36Sopenharmony_ci unsigned int index, 53262306a36Sopenharmony_ci const char *name, unsigned int flags, 53362306a36Sopenharmony_ci unsigned int num_descs, unsigned int desc_size, 53462306a36Sopenharmony_ci unsigned int cq_desc_size, 53562306a36Sopenharmony_ci unsigned int sg_desc_size, 53662306a36Sopenharmony_ci unsigned int pid, struct ionic_qcq **qcq) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 53962306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 54062306a36Sopenharmony_ci void *q_base, *cq_base, *sg_base; 54162306a36Sopenharmony_ci dma_addr_t cq_base_pa = 0; 54262306a36Sopenharmony_ci dma_addr_t sg_base_pa = 0; 54362306a36Sopenharmony_ci dma_addr_t q_base_pa = 0; 54462306a36Sopenharmony_ci struct ionic_qcq *new; 54562306a36Sopenharmony_ci int err; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci *qcq = NULL; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL); 55062306a36Sopenharmony_ci if (!new) { 55162306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate queue structure\n"); 55262306a36Sopenharmony_ci err = -ENOMEM; 55362306a36Sopenharmony_ci goto err_out; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci new->q.dev = dev; 55762306a36Sopenharmony_ci new->flags = flags; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci new->q.info = vcalloc(num_descs, sizeof(*new->q.info)); 56062306a36Sopenharmony_ci if (!new->q.info) { 56162306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate queue info\n"); 56262306a36Sopenharmony_ci err = -ENOMEM; 56362306a36Sopenharmony_ci goto err_out_free_qcq; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci new->q.type = type; 56762306a36Sopenharmony_ci new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci err = ionic_q_init(lif, idev, &new->q, index, name, num_descs, 57062306a36Sopenharmony_ci desc_size, sg_desc_size, pid); 57162306a36Sopenharmony_ci if (err) { 57262306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot initialize queue\n"); 57362306a36Sopenharmony_ci goto err_out_free_q_info; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci err = ionic_alloc_qcq_interrupt(lif, new); 57762306a36Sopenharmony_ci if (err) 57862306a36Sopenharmony_ci goto err_out; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci new->cq.info = vcalloc(num_descs, sizeof(*new->cq.info)); 58162306a36Sopenharmony_ci if (!new->cq.info) { 58262306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate completion queue info\n"); 58362306a36Sopenharmony_ci err = -ENOMEM; 58462306a36Sopenharmony_ci goto err_out_free_irq; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size); 58862306a36Sopenharmony_ci if (err) { 58962306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot initialize completion queue\n"); 59062306a36Sopenharmony_ci goto err_out_free_cq_info; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (flags & IONIC_QCQ_F_NOTIFYQ) { 59462306a36Sopenharmony_ci int q_size; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* q & cq need to be contiguous in NotifyQ, so alloc it all in q 59762306a36Sopenharmony_ci * and don't alloc qc. We leave new->qc_size and new->qc_base 59862306a36Sopenharmony_ci * as 0 to be sure we don't try to free it later. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci q_size = ALIGN(num_descs * desc_size, PAGE_SIZE); 60162306a36Sopenharmony_ci new->q_size = PAGE_SIZE + q_size + 60262306a36Sopenharmony_ci ALIGN(num_descs * cq_desc_size, PAGE_SIZE); 60362306a36Sopenharmony_ci new->q_base = dma_alloc_coherent(dev, new->q_size, 60462306a36Sopenharmony_ci &new->q_base_pa, GFP_KERNEL); 60562306a36Sopenharmony_ci if (!new->q_base) { 60662306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate qcq DMA memory\n"); 60762306a36Sopenharmony_ci err = -ENOMEM; 60862306a36Sopenharmony_ci goto err_out_free_cq_info; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci q_base = PTR_ALIGN(new->q_base, PAGE_SIZE); 61162306a36Sopenharmony_ci q_base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); 61262306a36Sopenharmony_ci ionic_q_map(&new->q, q_base, q_base_pa); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci cq_base = PTR_ALIGN(q_base + q_size, PAGE_SIZE); 61562306a36Sopenharmony_ci cq_base_pa = ALIGN(new->q_base_pa + q_size, PAGE_SIZE); 61662306a36Sopenharmony_ci ionic_cq_map(&new->cq, cq_base, cq_base_pa); 61762306a36Sopenharmony_ci ionic_cq_bind(&new->cq, &new->q); 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci /* regular DMA q descriptors */ 62062306a36Sopenharmony_ci new->q_size = PAGE_SIZE + (num_descs * desc_size); 62162306a36Sopenharmony_ci new->q_base = dma_alloc_coherent(dev, new->q_size, &new->q_base_pa, 62262306a36Sopenharmony_ci GFP_KERNEL); 62362306a36Sopenharmony_ci if (!new->q_base) { 62462306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n"); 62562306a36Sopenharmony_ci err = -ENOMEM; 62662306a36Sopenharmony_ci goto err_out_free_cq_info; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci q_base = PTR_ALIGN(new->q_base, PAGE_SIZE); 62962306a36Sopenharmony_ci q_base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); 63062306a36Sopenharmony_ci ionic_q_map(&new->q, q_base, q_base_pa); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (flags & IONIC_QCQ_F_CMB_RINGS) { 63362306a36Sopenharmony_ci /* on-chip CMB q descriptors */ 63462306a36Sopenharmony_ci new->cmb_q_size = num_descs * desc_size; 63562306a36Sopenharmony_ci new->cmb_order = order_base_2(new->cmb_q_size / PAGE_SIZE); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci err = ionic_get_cmb(lif, &new->cmb_pgid, &new->cmb_q_base_pa, 63862306a36Sopenharmony_ci new->cmb_order); 63962306a36Sopenharmony_ci if (err) { 64062306a36Sopenharmony_ci netdev_err(lif->netdev, 64162306a36Sopenharmony_ci "Cannot allocate queue order %d from cmb: err %d\n", 64262306a36Sopenharmony_ci new->cmb_order, err); 64362306a36Sopenharmony_ci goto err_out_free_q; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci new->cmb_q_base = ioremap_wc(new->cmb_q_base_pa, new->cmb_q_size); 64762306a36Sopenharmony_ci if (!new->cmb_q_base) { 64862306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot map queue from cmb\n"); 64962306a36Sopenharmony_ci ionic_put_cmb(lif, new->cmb_pgid, new->cmb_order); 65062306a36Sopenharmony_ci err = -ENOMEM; 65162306a36Sopenharmony_ci goto err_out_free_q; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci new->cmb_q_base_pa -= idev->phy_cmb_pages; 65562306a36Sopenharmony_ci ionic_q_cmb_map(&new->q, new->cmb_q_base, new->cmb_q_base_pa); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* cq DMA descriptors */ 65962306a36Sopenharmony_ci new->cq_size = PAGE_SIZE + (num_descs * cq_desc_size); 66062306a36Sopenharmony_ci new->cq_base = dma_alloc_coherent(dev, new->cq_size, &new->cq_base_pa, 66162306a36Sopenharmony_ci GFP_KERNEL); 66262306a36Sopenharmony_ci if (!new->cq_base) { 66362306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate cq DMA memory\n"); 66462306a36Sopenharmony_ci err = -ENOMEM; 66562306a36Sopenharmony_ci goto err_out_free_q; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci cq_base = PTR_ALIGN(new->cq_base, PAGE_SIZE); 66862306a36Sopenharmony_ci cq_base_pa = ALIGN(new->cq_base_pa, PAGE_SIZE); 66962306a36Sopenharmony_ci ionic_cq_map(&new->cq, cq_base, cq_base_pa); 67062306a36Sopenharmony_ci ionic_cq_bind(&new->cq, &new->q); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (flags & IONIC_QCQ_F_SG) { 67462306a36Sopenharmony_ci new->sg_size = PAGE_SIZE + (num_descs * sg_desc_size); 67562306a36Sopenharmony_ci new->sg_base = dma_alloc_coherent(dev, new->sg_size, &new->sg_base_pa, 67662306a36Sopenharmony_ci GFP_KERNEL); 67762306a36Sopenharmony_ci if (!new->sg_base) { 67862306a36Sopenharmony_ci netdev_err(lif->netdev, "Cannot allocate sg DMA memory\n"); 67962306a36Sopenharmony_ci err = -ENOMEM; 68062306a36Sopenharmony_ci goto err_out_free_cq; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci sg_base = PTR_ALIGN(new->sg_base, PAGE_SIZE); 68362306a36Sopenharmony_ci sg_base_pa = ALIGN(new->sg_base_pa, PAGE_SIZE); 68462306a36Sopenharmony_ci ionic_q_sg_map(&new->q, sg_base, sg_base_pa); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci INIT_WORK(&new->dim.work, ionic_dim_work); 68862306a36Sopenharmony_ci new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci *qcq = new; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cierr_out_free_cq: 69562306a36Sopenharmony_ci dma_free_coherent(dev, new->cq_size, new->cq_base, new->cq_base_pa); 69662306a36Sopenharmony_cierr_out_free_q: 69762306a36Sopenharmony_ci if (new->cmb_q_base) { 69862306a36Sopenharmony_ci iounmap(new->cmb_q_base); 69962306a36Sopenharmony_ci ionic_put_cmb(lif, new->cmb_pgid, new->cmb_order); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci dma_free_coherent(dev, new->q_size, new->q_base, new->q_base_pa); 70262306a36Sopenharmony_cierr_out_free_cq_info: 70362306a36Sopenharmony_ci vfree(new->cq.info); 70462306a36Sopenharmony_cierr_out_free_irq: 70562306a36Sopenharmony_ci if (flags & IONIC_QCQ_F_INTR) { 70662306a36Sopenharmony_ci devm_free_irq(dev, new->intr.vector, &new->napi); 70762306a36Sopenharmony_ci ionic_intr_free(lif->ionic, new->intr.index); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_cierr_out_free_q_info: 71062306a36Sopenharmony_ci vfree(new->q.info); 71162306a36Sopenharmony_cierr_out_free_qcq: 71262306a36Sopenharmony_ci devm_kfree(dev, new); 71362306a36Sopenharmony_cierr_out: 71462306a36Sopenharmony_ci dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err); 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int ionic_qcqs_alloc(struct ionic_lif *lif) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 72162306a36Sopenharmony_ci unsigned int flags; 72262306a36Sopenharmony_ci int err; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci flags = IONIC_QCQ_F_INTR; 72562306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_ADMINQ, 0, "admin", flags, 72662306a36Sopenharmony_ci IONIC_ADMINQ_LENGTH, 72762306a36Sopenharmony_ci sizeof(struct ionic_admin_cmd), 72862306a36Sopenharmony_ci sizeof(struct ionic_admin_comp), 72962306a36Sopenharmony_ci 0, lif->kern_pid, &lif->adminqcq); 73062306a36Sopenharmony_ci if (err) 73162306a36Sopenharmony_ci return err; 73262306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->adminqcq); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (lif->ionic->nnqs_per_lif) { 73562306a36Sopenharmony_ci flags = IONIC_QCQ_F_NOTIFYQ; 73662306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0, "notifyq", 73762306a36Sopenharmony_ci flags, IONIC_NOTIFYQ_LENGTH, 73862306a36Sopenharmony_ci sizeof(struct ionic_notifyq_cmd), 73962306a36Sopenharmony_ci sizeof(union ionic_notifyq_comp), 74062306a36Sopenharmony_ci 0, lif->kern_pid, &lif->notifyqcq); 74162306a36Sopenharmony_ci if (err) 74262306a36Sopenharmony_ci goto err_out; 74362306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->notifyqcq); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Let the notifyq ride on the adminq interrupt */ 74662306a36Sopenharmony_ci ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci err = -ENOMEM; 75062306a36Sopenharmony_ci lif->txqcqs = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif, 75162306a36Sopenharmony_ci sizeof(*lif->txqcqs), GFP_KERNEL); 75262306a36Sopenharmony_ci if (!lif->txqcqs) 75362306a36Sopenharmony_ci goto err_out; 75462306a36Sopenharmony_ci lif->rxqcqs = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif, 75562306a36Sopenharmony_ci sizeof(*lif->rxqcqs), GFP_KERNEL); 75662306a36Sopenharmony_ci if (!lif->rxqcqs) 75762306a36Sopenharmony_ci goto err_out; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci lif->txqstats = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif + 1, 76062306a36Sopenharmony_ci sizeof(*lif->txqstats), GFP_KERNEL); 76162306a36Sopenharmony_ci if (!lif->txqstats) 76262306a36Sopenharmony_ci goto err_out; 76362306a36Sopenharmony_ci lif->rxqstats = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif + 1, 76462306a36Sopenharmony_ci sizeof(*lif->rxqstats), GFP_KERNEL); 76562306a36Sopenharmony_ci if (!lif->rxqstats) 76662306a36Sopenharmony_ci goto err_out; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cierr_out: 77162306a36Sopenharmony_ci ionic_qcqs_free(lif); 77262306a36Sopenharmony_ci return err; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic void ionic_qcq_sanitize(struct ionic_qcq *qcq) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci qcq->q.tail_idx = 0; 77862306a36Sopenharmony_ci qcq->q.head_idx = 0; 77962306a36Sopenharmony_ci qcq->cq.tail_idx = 0; 78062306a36Sopenharmony_ci qcq->cq.done_color = 1; 78162306a36Sopenharmony_ci memset(qcq->q_base, 0, qcq->q_size); 78262306a36Sopenharmony_ci if (qcq->cmb_q_base) 78362306a36Sopenharmony_ci memset_io(qcq->cmb_q_base, 0, qcq->cmb_q_size); 78462306a36Sopenharmony_ci memset(qcq->cq_base, 0, qcq->cq_size); 78562306a36Sopenharmony_ci memset(qcq->sg_base, 0, qcq->sg_size); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 79162306a36Sopenharmony_ci struct ionic_queue *q = &qcq->q; 79262306a36Sopenharmony_ci struct ionic_cq *cq = &qcq->cq; 79362306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 79462306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 79562306a36Sopenharmony_ci .cmd.q_init = { 79662306a36Sopenharmony_ci .opcode = IONIC_CMD_Q_INIT, 79762306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 79862306a36Sopenharmony_ci .type = q->type, 79962306a36Sopenharmony_ci .ver = lif->qtype_info[q->type].version, 80062306a36Sopenharmony_ci .index = cpu_to_le32(q->index), 80162306a36Sopenharmony_ci .flags = cpu_to_le16(IONIC_QINIT_F_IRQ | 80262306a36Sopenharmony_ci IONIC_QINIT_F_SG), 80362306a36Sopenharmony_ci .intr_index = cpu_to_le16(qcq->intr.index), 80462306a36Sopenharmony_ci .pid = cpu_to_le16(q->pid), 80562306a36Sopenharmony_ci .ring_size = ilog2(q->num_descs), 80662306a36Sopenharmony_ci .ring_base = cpu_to_le64(q->base_pa), 80762306a36Sopenharmony_ci .cq_ring_base = cpu_to_le64(cq->base_pa), 80862306a36Sopenharmony_ci .sg_ring_base = cpu_to_le64(q->sg_base_pa), 80962306a36Sopenharmony_ci .features = cpu_to_le64(q->features), 81062306a36Sopenharmony_ci }, 81162306a36Sopenharmony_ci }; 81262306a36Sopenharmony_ci int err; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) { 81562306a36Sopenharmony_ci ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB); 81662306a36Sopenharmony_ci ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci dev_dbg(dev, "txq_init.pid %d\n", ctx.cmd.q_init.pid); 82062306a36Sopenharmony_ci dev_dbg(dev, "txq_init.index %d\n", ctx.cmd.q_init.index); 82162306a36Sopenharmony_ci dev_dbg(dev, "txq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base); 82262306a36Sopenharmony_ci dev_dbg(dev, "txq_init.ring_size %d\n", ctx.cmd.q_init.ring_size); 82362306a36Sopenharmony_ci dev_dbg(dev, "txq_init.cq_ring_base 0x%llx\n", ctx.cmd.q_init.cq_ring_base); 82462306a36Sopenharmony_ci dev_dbg(dev, "txq_init.sg_ring_base 0x%llx\n", ctx.cmd.q_init.sg_ring_base); 82562306a36Sopenharmony_ci dev_dbg(dev, "txq_init.flags 0x%x\n", ctx.cmd.q_init.flags); 82662306a36Sopenharmony_ci dev_dbg(dev, "txq_init.ver %d\n", ctx.cmd.q_init.ver); 82762306a36Sopenharmony_ci dev_dbg(dev, "txq_init.intr_index %d\n", ctx.cmd.q_init.intr_index); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci ionic_qcq_sanitize(qcq); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 83262306a36Sopenharmony_ci if (err) 83362306a36Sopenharmony_ci return err; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci q->hw_type = ctx.comp.q_init.hw_type; 83662306a36Sopenharmony_ci q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index); 83762306a36Sopenharmony_ci q->dbval = IONIC_DBELL_QID(q->hw_index); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci dev_dbg(dev, "txq->hw_type %d\n", q->hw_type); 84062306a36Sopenharmony_ci dev_dbg(dev, "txq->hw_index %d\n", q->hw_index); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci q->dbell_deadline = IONIC_TX_DOORBELL_DEADLINE; 84362306a36Sopenharmony_ci q->dbell_jiffies = jiffies; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) { 84662306a36Sopenharmony_ci netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi); 84762306a36Sopenharmony_ci qcq->napi_qcq = qcq; 84862306a36Sopenharmony_ci timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci qcq->flags |= IONIC_QCQ_F_INITED; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 85962306a36Sopenharmony_ci struct ionic_queue *q = &qcq->q; 86062306a36Sopenharmony_ci struct ionic_cq *cq = &qcq->cq; 86162306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 86262306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 86362306a36Sopenharmony_ci .cmd.q_init = { 86462306a36Sopenharmony_ci .opcode = IONIC_CMD_Q_INIT, 86562306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 86662306a36Sopenharmony_ci .type = q->type, 86762306a36Sopenharmony_ci .ver = lif->qtype_info[q->type].version, 86862306a36Sopenharmony_ci .index = cpu_to_le32(q->index), 86962306a36Sopenharmony_ci .flags = cpu_to_le16(IONIC_QINIT_F_IRQ | 87062306a36Sopenharmony_ci IONIC_QINIT_F_SG), 87162306a36Sopenharmony_ci .intr_index = cpu_to_le16(cq->bound_intr->index), 87262306a36Sopenharmony_ci .pid = cpu_to_le16(q->pid), 87362306a36Sopenharmony_ci .ring_size = ilog2(q->num_descs), 87462306a36Sopenharmony_ci .ring_base = cpu_to_le64(q->base_pa), 87562306a36Sopenharmony_ci .cq_ring_base = cpu_to_le64(cq->base_pa), 87662306a36Sopenharmony_ci .sg_ring_base = cpu_to_le64(q->sg_base_pa), 87762306a36Sopenharmony_ci .features = cpu_to_le64(q->features), 87862306a36Sopenharmony_ci }, 87962306a36Sopenharmony_ci }; 88062306a36Sopenharmony_ci int err; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) { 88362306a36Sopenharmony_ci ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB); 88462306a36Sopenharmony_ci ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.pid %d\n", ctx.cmd.q_init.pid); 88862306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.index %d\n", ctx.cmd.q_init.index); 88962306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base); 89062306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.ring_size %d\n", ctx.cmd.q_init.ring_size); 89162306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.flags 0x%x\n", ctx.cmd.q_init.flags); 89262306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.ver %d\n", ctx.cmd.q_init.ver); 89362306a36Sopenharmony_ci dev_dbg(dev, "rxq_init.intr_index %d\n", ctx.cmd.q_init.intr_index); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ionic_qcq_sanitize(qcq); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 89862306a36Sopenharmony_ci if (err) 89962306a36Sopenharmony_ci return err; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci q->hw_type = ctx.comp.q_init.hw_type; 90262306a36Sopenharmony_ci q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index); 90362306a36Sopenharmony_ci q->dbval = IONIC_DBELL_QID(q->hw_index); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci dev_dbg(dev, "rxq->hw_type %d\n", q->hw_type); 90662306a36Sopenharmony_ci dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE; 90962306a36Sopenharmony_ci q->dbell_jiffies = jiffies; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 91262306a36Sopenharmony_ci netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi); 91362306a36Sopenharmony_ci else 91462306a36Sopenharmony_ci netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci qcq->napi_qcq = qcq; 91762306a36Sopenharmony_ci timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci qcq->flags |= IONIC_QCQ_F_INITED; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciint ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz; 92762306a36Sopenharmony_ci unsigned int txq_i, flags; 92862306a36Sopenharmony_ci struct ionic_qcq *txq; 92962306a36Sopenharmony_ci u64 features; 93062306a36Sopenharmony_ci int err; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (lif->hwstamp_txq) 93362306a36Sopenharmony_ci return 0; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci features = IONIC_Q_F_2X_CQ_DESC | IONIC_TXQ_F_HWSTAMP; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci num_desc = IONIC_MIN_TXRX_DESC; 93862306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_txq_desc); 93962306a36Sopenharmony_ci comp_sz = 2 * sizeof(struct ionic_txq_comp); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && 94262306a36Sopenharmony_ci lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == sizeof(struct ionic_txq_sg_desc_v1)) 94362306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); 94462306a36Sopenharmony_ci else 94562306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci txq_i = lif->ionic->ntxqs_per_lif; 94862306a36Sopenharmony_ci flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags, 95162306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 95262306a36Sopenharmony_ci lif->kern_pid, &txq); 95362306a36Sopenharmony_ci if (err) 95462306a36Sopenharmony_ci goto err_qcq_alloc; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci txq->q.features = features; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci ionic_link_qcq_interrupts(lif->adminqcq, txq); 95962306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, txq); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci lif->hwstamp_txq = txq; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (netif_running(lif->netdev)) { 96462306a36Sopenharmony_ci err = ionic_lif_txq_init(lif, txq); 96562306a36Sopenharmony_ci if (err) 96662306a36Sopenharmony_ci goto err_qcq_init; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_UP, lif->state)) { 96962306a36Sopenharmony_ci err = ionic_qcq_enable(txq); 97062306a36Sopenharmony_ci if (err) 97162306a36Sopenharmony_ci goto err_qcq_enable; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cierr_qcq_enable: 97862306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, txq); 97962306a36Sopenharmony_cierr_qcq_init: 98062306a36Sopenharmony_ci lif->hwstamp_txq = NULL; 98162306a36Sopenharmony_ci ionic_debugfs_del_qcq(txq); 98262306a36Sopenharmony_ci ionic_qcq_free(lif, txq); 98362306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, txq); 98462306a36Sopenharmony_cierr_qcq_alloc: 98562306a36Sopenharmony_ci return err; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ciint ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz; 99162306a36Sopenharmony_ci unsigned int rxq_i, flags; 99262306a36Sopenharmony_ci struct ionic_qcq *rxq; 99362306a36Sopenharmony_ci u64 features; 99462306a36Sopenharmony_ci int err; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (lif->hwstamp_rxq) 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci num_desc = IONIC_MIN_TXRX_DESC; 100262306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_rxq_desc); 100362306a36Sopenharmony_ci comp_sz = 2 * sizeof(struct ionic_rxq_comp); 100462306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci rxq_i = lif->ionic->nrxqs_per_lif; 100762306a36Sopenharmony_ci flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags, 101062306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 101162306a36Sopenharmony_ci lif->kern_pid, &rxq); 101262306a36Sopenharmony_ci if (err) 101362306a36Sopenharmony_ci goto err_qcq_alloc; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci rxq->q.features = features; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci ionic_link_qcq_interrupts(lif->adminqcq, rxq); 101862306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, rxq); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci lif->hwstamp_rxq = rxq; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (netif_running(lif->netdev)) { 102362306a36Sopenharmony_ci err = ionic_lif_rxq_init(lif, rxq); 102462306a36Sopenharmony_ci if (err) 102562306a36Sopenharmony_ci goto err_qcq_init; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_UP, lif->state)) { 102862306a36Sopenharmony_ci ionic_rx_fill(&rxq->q); 102962306a36Sopenharmony_ci err = ionic_qcq_enable(rxq); 103062306a36Sopenharmony_ci if (err) 103162306a36Sopenharmony_ci goto err_qcq_enable; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cierr_qcq_enable: 103862306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, rxq); 103962306a36Sopenharmony_cierr_qcq_init: 104062306a36Sopenharmony_ci lif->hwstamp_rxq = NULL; 104162306a36Sopenharmony_ci ionic_debugfs_del_qcq(rxq); 104262306a36Sopenharmony_ci ionic_qcq_free(lif, rxq); 104362306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, rxq); 104462306a36Sopenharmony_cierr_qcq_alloc: 104562306a36Sopenharmony_ci return err; 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ciint ionic_lif_config_hwstamp_rxq_all(struct ionic_lif *lif, bool rx_all) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct ionic_queue_params qparam; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ionic_init_queue_params(lif, &qparam); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (rx_all) 105562306a36Sopenharmony_ci qparam.rxq_features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP; 105662306a36Sopenharmony_ci else 105762306a36Sopenharmony_ci qparam.rxq_features = 0; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* if we're not running, just set the values and return */ 106062306a36Sopenharmony_ci if (!netif_running(lif->netdev)) { 106162306a36Sopenharmony_ci lif->rxq_features = qparam.rxq_features; 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return ionic_reconfigure_queues(lif, &qparam); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ciint ionic_lif_set_hwstamp_txmode(struct ionic_lif *lif, u16 txstamp_mode) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 107162306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 107262306a36Sopenharmony_ci .cmd.lif_setattr = { 107362306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 107462306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 107562306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_TXSTAMP, 107662306a36Sopenharmony_ci .txstamp_mode = cpu_to_le16(txstamp_mode), 107762306a36Sopenharmony_ci }, 107862306a36Sopenharmony_ci }; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return ionic_adminq_post_wait(lif, &ctx); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic void ionic_lif_del_hwstamp_rxfilt(struct ionic_lif *lif) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 108662306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 108762306a36Sopenharmony_ci .cmd.rx_filter_del = { 108862306a36Sopenharmony_ci .opcode = IONIC_CMD_RX_FILTER_DEL, 108962306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 109062306a36Sopenharmony_ci }, 109162306a36Sopenharmony_ci }; 109262306a36Sopenharmony_ci struct ionic_rx_filter *f; 109362306a36Sopenharmony_ci u32 filter_id; 109462306a36Sopenharmony_ci int err; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci spin_lock_bh(&lif->rx_filters.lock); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci f = ionic_rx_filter_rxsteer(lif); 109962306a36Sopenharmony_ci if (!f) { 110062306a36Sopenharmony_ci spin_unlock_bh(&lif->rx_filters.lock); 110162306a36Sopenharmony_ci return; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci filter_id = f->filter_id; 110562306a36Sopenharmony_ci ionic_rx_filter_free(lif, f); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci spin_unlock_bh(&lif->rx_filters.lock); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci netdev_dbg(lif->netdev, "rx_filter del RXSTEER (id %d)\n", filter_id); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(filter_id); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 111462306a36Sopenharmony_ci if (err && err != -EEXIST) 111562306a36Sopenharmony_ci netdev_dbg(lif->netdev, "failed to delete rx_filter RXSTEER (id %d)\n", filter_id); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int ionic_lif_add_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 112162306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 112262306a36Sopenharmony_ci .cmd.rx_filter_add = { 112362306a36Sopenharmony_ci .opcode = IONIC_CMD_RX_FILTER_ADD, 112462306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 112562306a36Sopenharmony_ci .match = cpu_to_le16(IONIC_RX_FILTER_STEER_PKTCLASS), 112662306a36Sopenharmony_ci .pkt_class = cpu_to_le64(pkt_class), 112762306a36Sopenharmony_ci }, 112862306a36Sopenharmony_ci }; 112962306a36Sopenharmony_ci u8 qtype; 113062306a36Sopenharmony_ci u32 qid; 113162306a36Sopenharmony_ci int err; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (!lif->hwstamp_rxq) 113462306a36Sopenharmony_ci return -EINVAL; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci qtype = lif->hwstamp_rxq->q.type; 113762306a36Sopenharmony_ci ctx.cmd.rx_filter_add.qtype = qtype; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci qid = lif->hwstamp_rxq->q.index; 114062306a36Sopenharmony_ci ctx.cmd.rx_filter_add.qid = cpu_to_le32(qid); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci netdev_dbg(lif->netdev, "rx_filter add RXSTEER\n"); 114362306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 114462306a36Sopenharmony_ci if (err && err != -EEXIST) 114562306a36Sopenharmony_ci return err; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci spin_lock_bh(&lif->rx_filters.lock); 114862306a36Sopenharmony_ci err = ionic_rx_filter_save(lif, 0, qid, 0, &ctx, IONIC_FILTER_STATE_SYNCED); 114962306a36Sopenharmony_ci spin_unlock_bh(&lif->rx_filters.lock); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci return err; 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ciint ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci ionic_lif_del_hwstamp_rxfilt(lif); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!pkt_class) 115962306a36Sopenharmony_ci return 0; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return ionic_lif_add_hwstamp_rxfilt(lif, pkt_class); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic bool ionic_notifyq_service(struct ionic_cq *cq, 116562306a36Sopenharmony_ci struct ionic_cq_info *cq_info) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci union ionic_notifyq_comp *comp = cq_info->cq_desc; 116862306a36Sopenharmony_ci struct ionic_deferred_work *work; 116962306a36Sopenharmony_ci struct net_device *netdev; 117062306a36Sopenharmony_ci struct ionic_queue *q; 117162306a36Sopenharmony_ci struct ionic_lif *lif; 117262306a36Sopenharmony_ci u64 eid; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci q = cq->bound_q; 117562306a36Sopenharmony_ci lif = q->info[0].cb_arg; 117662306a36Sopenharmony_ci netdev = lif->netdev; 117762306a36Sopenharmony_ci eid = le64_to_cpu(comp->event.eid); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Have we run out of new completions to process? */ 118062306a36Sopenharmony_ci if ((s64)(eid - lif->last_eid) <= 0) 118162306a36Sopenharmony_ci return false; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci lif->last_eid = eid; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci dev_dbg(lif->ionic->dev, "notifyq event:\n"); 118662306a36Sopenharmony_ci dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, 118762306a36Sopenharmony_ci comp, sizeof(*comp), true); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci switch (le16_to_cpu(comp->event.ecode)) { 119062306a36Sopenharmony_ci case IONIC_EVENT_LINK_CHANGE: 119162306a36Sopenharmony_ci ionic_link_status_check_request(lif, CAN_NOT_SLEEP); 119262306a36Sopenharmony_ci break; 119362306a36Sopenharmony_ci case IONIC_EVENT_RESET: 119462306a36Sopenharmony_ci if (lif->ionic->idev.fw_status_ready && 119562306a36Sopenharmony_ci !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && 119662306a36Sopenharmony_ci !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { 119762306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_ATOMIC); 119862306a36Sopenharmony_ci if (!work) { 119962306a36Sopenharmony_ci netdev_err(lif->netdev, "Reset event dropped\n"); 120062306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); 120162306a36Sopenharmony_ci } else { 120262306a36Sopenharmony_ci work->type = IONIC_DW_TYPE_LIF_RESET; 120362306a36Sopenharmony_ci ionic_lif_deferred_enqueue(&lif->deferred, work); 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", 120962306a36Sopenharmony_ci comp->event.ecode, eid); 121062306a36Sopenharmony_ci break; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return true; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic bool ionic_adminq_service(struct ionic_cq *cq, 121762306a36Sopenharmony_ci struct ionic_cq_info *cq_info) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct ionic_admin_comp *comp = cq_info->cq_desc; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (!color_match(comp->color, cq->done_color)) 122262306a36Sopenharmony_ci return false; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci ionic_q_service(cq->bound_q, cq_info, le16_to_cpu(comp->comp_index)); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci return true; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic int ionic_adminq_napi(struct napi_struct *napi, int budget) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct ionic_intr_info *intr = napi_to_cq(napi)->bound_intr; 123262306a36Sopenharmony_ci struct ionic_lif *lif = napi_to_cq(napi)->lif; 123362306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 123462306a36Sopenharmony_ci unsigned long irqflags; 123562306a36Sopenharmony_ci unsigned int flags = 0; 123662306a36Sopenharmony_ci bool resched = false; 123762306a36Sopenharmony_ci int rx_work = 0; 123862306a36Sopenharmony_ci int tx_work = 0; 123962306a36Sopenharmony_ci int n_work = 0; 124062306a36Sopenharmony_ci int a_work = 0; 124162306a36Sopenharmony_ci int work_done; 124262306a36Sopenharmony_ci int credits; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (lif->notifyqcq && lif->notifyqcq->flags & IONIC_QCQ_F_INITED) 124562306a36Sopenharmony_ci n_work = ionic_cq_service(&lif->notifyqcq->cq, budget, 124662306a36Sopenharmony_ci ionic_notifyq_service, NULL, NULL); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_lock_irqsave(&lif->adminq_lock, irqflags); 124962306a36Sopenharmony_ci if (lif->adminqcq && lif->adminqcq->flags & IONIC_QCQ_F_INITED) 125062306a36Sopenharmony_ci a_work = ionic_cq_service(&lif->adminqcq->cq, budget, 125162306a36Sopenharmony_ci ionic_adminq_service, NULL, NULL); 125262306a36Sopenharmony_ci spin_unlock_irqrestore(&lif->adminq_lock, irqflags); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (lif->hwstamp_rxq) 125562306a36Sopenharmony_ci rx_work = ionic_cq_service(&lif->hwstamp_rxq->cq, budget, 125662306a36Sopenharmony_ci ionic_rx_service, NULL, NULL); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (lif->hwstamp_txq) 125962306a36Sopenharmony_ci tx_work = ionic_cq_service(&lif->hwstamp_txq->cq, budget, 126062306a36Sopenharmony_ci ionic_tx_service, NULL, NULL); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci work_done = max(max(n_work, a_work), max(rx_work, tx_work)); 126362306a36Sopenharmony_ci if (work_done < budget && napi_complete_done(napi, work_done)) { 126462306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 126562306a36Sopenharmony_ci intr->rearm_count++; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (work_done || flags) { 126962306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 127062306a36Sopenharmony_ci credits = n_work + a_work + rx_work + tx_work; 127162306a36Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, intr->index, credits, flags); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (!a_work && ionic_adminq_poke_doorbell(&lif->adminqcq->q)) 127562306a36Sopenharmony_ci resched = true; 127662306a36Sopenharmony_ci if (lif->hwstamp_rxq && !rx_work && ionic_rxq_poke_doorbell(&lif->hwstamp_rxq->q)) 127762306a36Sopenharmony_ci resched = true; 127862306a36Sopenharmony_ci if (lif->hwstamp_txq && !tx_work && ionic_txq_poke_doorbell(&lif->hwstamp_txq->q)) 127962306a36Sopenharmony_ci resched = true; 128062306a36Sopenharmony_ci if (resched) 128162306a36Sopenharmony_ci mod_timer(&lif->adminqcq->napi_deadline, 128262306a36Sopenharmony_ci jiffies + IONIC_NAPI_DEADLINE); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci return work_done; 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_civoid ionic_get_stats64(struct net_device *netdev, 128862306a36Sopenharmony_ci struct rtnl_link_stats64 *ns) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 129162306a36Sopenharmony_ci struct ionic_lif_stats *ls; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci memset(ns, 0, sizeof(*ns)); 129462306a36Sopenharmony_ci ls = &lif->info->stats; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci ns->rx_packets = le64_to_cpu(ls->rx_ucast_packets) + 129762306a36Sopenharmony_ci le64_to_cpu(ls->rx_mcast_packets) + 129862306a36Sopenharmony_ci le64_to_cpu(ls->rx_bcast_packets); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci ns->tx_packets = le64_to_cpu(ls->tx_ucast_packets) + 130162306a36Sopenharmony_ci le64_to_cpu(ls->tx_mcast_packets) + 130262306a36Sopenharmony_ci le64_to_cpu(ls->tx_bcast_packets); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci ns->rx_bytes = le64_to_cpu(ls->rx_ucast_bytes) + 130562306a36Sopenharmony_ci le64_to_cpu(ls->rx_mcast_bytes) + 130662306a36Sopenharmony_ci le64_to_cpu(ls->rx_bcast_bytes); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci ns->tx_bytes = le64_to_cpu(ls->tx_ucast_bytes) + 130962306a36Sopenharmony_ci le64_to_cpu(ls->tx_mcast_bytes) + 131062306a36Sopenharmony_ci le64_to_cpu(ls->tx_bcast_bytes); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ns->rx_dropped = le64_to_cpu(ls->rx_ucast_drop_packets) + 131362306a36Sopenharmony_ci le64_to_cpu(ls->rx_mcast_drop_packets) + 131462306a36Sopenharmony_ci le64_to_cpu(ls->rx_bcast_drop_packets); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci ns->tx_dropped = le64_to_cpu(ls->tx_ucast_drop_packets) + 131762306a36Sopenharmony_ci le64_to_cpu(ls->tx_mcast_drop_packets) + 131862306a36Sopenharmony_ci le64_to_cpu(ls->tx_bcast_drop_packets); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci ns->multicast = le64_to_cpu(ls->rx_mcast_packets); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci ns->rx_over_errors = le64_to_cpu(ls->rx_queue_empty); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci ns->rx_missed_errors = le64_to_cpu(ls->rx_dma_error) + 132562306a36Sopenharmony_ci le64_to_cpu(ls->rx_queue_disabled) + 132662306a36Sopenharmony_ci le64_to_cpu(ls->rx_desc_fetch_error) + 132762306a36Sopenharmony_ci le64_to_cpu(ls->rx_desc_data_error); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci ns->tx_aborted_errors = le64_to_cpu(ls->tx_dma_error) + 133062306a36Sopenharmony_ci le64_to_cpu(ls->tx_queue_disabled) + 133162306a36Sopenharmony_ci le64_to_cpu(ls->tx_desc_fetch_error) + 133262306a36Sopenharmony_ci le64_to_cpu(ls->tx_desc_data_error); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci ns->rx_errors = ns->rx_over_errors + 133562306a36Sopenharmony_ci ns->rx_missed_errors; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ns->tx_errors = ns->tx_aborted_errors; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic int ionic_addr_add(struct net_device *netdev, const u8 *addr) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci return ionic_lif_list_addr(netdev_priv(netdev), addr, ADD_ADDR); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic int ionic_addr_del(struct net_device *netdev, const u8 *addr) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci /* Don't delete our own address from the uc list */ 134862306a36Sopenharmony_ci if (ether_addr_equal(addr, netdev->dev_addr)) 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci return ionic_lif_list_addr(netdev_priv(netdev), addr, DEL_ADDR); 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_civoid ionic_lif_rx_mode(struct ionic_lif *lif) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci struct net_device *netdev = lif->netdev; 135762306a36Sopenharmony_ci unsigned int nfilters; 135862306a36Sopenharmony_ci unsigned int nd_flags; 135962306a36Sopenharmony_ci char buf[128]; 136062306a36Sopenharmony_ci u16 rx_mode; 136162306a36Sopenharmony_ci int i; 136262306a36Sopenharmony_ci#define REMAIN(__x) (sizeof(buf) - (__x)) 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci mutex_lock(&lif->config_lock); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* grab the flags once for local use */ 136762306a36Sopenharmony_ci nd_flags = netdev->flags; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci rx_mode = IONIC_RX_MODE_F_UNICAST; 137062306a36Sopenharmony_ci rx_mode |= (nd_flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0; 137162306a36Sopenharmony_ci rx_mode |= (nd_flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0; 137262306a36Sopenharmony_ci rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0; 137362306a36Sopenharmony_ci rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* sync the filters */ 137662306a36Sopenharmony_ci ionic_rx_filter_sync(lif); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* check for overflow state 137962306a36Sopenharmony_ci * if so, we track that we overflowed and enable NIC PROMISC 138062306a36Sopenharmony_ci * else if the overflow is set and not needed 138162306a36Sopenharmony_ci * we remove our overflow flag and check the netdev flags 138262306a36Sopenharmony_ci * to see if we can disable NIC PROMISC 138362306a36Sopenharmony_ci */ 138462306a36Sopenharmony_ci nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (((lif->nucast + lif->nmcast) >= nfilters) || 138762306a36Sopenharmony_ci (lif->max_vlans && lif->nvlans >= lif->max_vlans)) { 138862306a36Sopenharmony_ci rx_mode |= IONIC_RX_MODE_F_PROMISC; 138962306a36Sopenharmony_ci rx_mode |= IONIC_RX_MODE_F_ALLMULTI; 139062306a36Sopenharmony_ci } else { 139162306a36Sopenharmony_ci if (!(nd_flags & IFF_PROMISC)) 139262306a36Sopenharmony_ci rx_mode &= ~IONIC_RX_MODE_F_PROMISC; 139362306a36Sopenharmony_ci if (!(nd_flags & IFF_ALLMULTI)) 139462306a36Sopenharmony_ci rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:", 139862306a36Sopenharmony_ci lif->rx_mode, rx_mode); 139962306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_UNICAST) 140062306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST"); 140162306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_MULTICAST) 140262306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST"); 140362306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_BROADCAST) 140462306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST"); 140562306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_PROMISC) 140662306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC"); 140762306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_ALLMULTI) 140862306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI"); 140962306a36Sopenharmony_ci if (rx_mode & IONIC_RX_MODE_F_RDMA_SNIFFER) 141062306a36Sopenharmony_ci i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_RDMA_SNIFFER"); 141162306a36Sopenharmony_ci netdev_dbg(netdev, "lif%d %s\n", lif->index, buf); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (lif->rx_mode != rx_mode) { 141462306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 141562306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 141662306a36Sopenharmony_ci .cmd.rx_mode_set = { 141762306a36Sopenharmony_ci .opcode = IONIC_CMD_RX_MODE_SET, 141862306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 141962306a36Sopenharmony_ci }, 142062306a36Sopenharmony_ci }; 142162306a36Sopenharmony_ci int err; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci ctx.cmd.rx_mode_set.rx_mode = cpu_to_le16(rx_mode); 142462306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 142562306a36Sopenharmony_ci if (err) 142662306a36Sopenharmony_ci netdev_warn(netdev, "set rx_mode 0x%04x failed: %d\n", 142762306a36Sopenharmony_ci rx_mode, err); 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci lif->rx_mode = rx_mode; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci mutex_unlock(&lif->config_lock); 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic void ionic_ndo_set_rx_mode(struct net_device *netdev) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 143862306a36Sopenharmony_ci struct ionic_deferred_work *work; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci /* Sync the kernel filter list with the driver filter list */ 144162306a36Sopenharmony_ci __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del); 144262306a36Sopenharmony_ci __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* Shove off the rest of the rxmode work to the work task 144562306a36Sopenharmony_ci * which will include syncing the filters to the firmware. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_ATOMIC); 144862306a36Sopenharmony_ci if (!work) { 144962306a36Sopenharmony_ci netdev_err(lif->netdev, "rxmode change dropped\n"); 145062306a36Sopenharmony_ci return; 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci work->type = IONIC_DW_TYPE_RX_MODE; 145362306a36Sopenharmony_ci netdev_dbg(lif->netdev, "deferred: rx_mode\n"); 145462306a36Sopenharmony_ci ionic_lif_deferred_enqueue(&lif->deferred, work); 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic __le64 ionic_netdev_features_to_nic(netdev_features_t features) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci u64 wanted = 0; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_TX) 146262306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_VLAN_TX_TAG; 146362306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 146462306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_VLAN_RX_STRIP; 146562306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_FILTER) 146662306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_VLAN_RX_FILTER; 146762306a36Sopenharmony_ci if (features & NETIF_F_RXHASH) 146862306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_RX_HASH; 146962306a36Sopenharmony_ci if (features & NETIF_F_RXCSUM) 147062306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_RX_CSUM; 147162306a36Sopenharmony_ci if (features & NETIF_F_SG) 147262306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TX_SG; 147362306a36Sopenharmony_ci if (features & NETIF_F_HW_CSUM) 147462306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TX_CSUM; 147562306a36Sopenharmony_ci if (features & NETIF_F_TSO) 147662306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO; 147762306a36Sopenharmony_ci if (features & NETIF_F_TSO6) 147862306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_IPV6; 147962306a36Sopenharmony_ci if (features & NETIF_F_TSO_ECN) 148062306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_ECN; 148162306a36Sopenharmony_ci if (features & NETIF_F_GSO_GRE) 148262306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_GRE; 148362306a36Sopenharmony_ci if (features & NETIF_F_GSO_GRE_CSUM) 148462306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_GRE_CSUM; 148562306a36Sopenharmony_ci if (features & NETIF_F_GSO_IPXIP4) 148662306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_IPXIP4; 148762306a36Sopenharmony_ci if (features & NETIF_F_GSO_IPXIP6) 148862306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_IPXIP6; 148962306a36Sopenharmony_ci if (features & NETIF_F_GSO_UDP_TUNNEL) 149062306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_UDP; 149162306a36Sopenharmony_ci if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) 149262306a36Sopenharmony_ci wanted |= IONIC_ETH_HW_TSO_UDP_CSUM; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci return cpu_to_le64(wanted); 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic int ionic_set_nic_features(struct ionic_lif *lif, 149862306a36Sopenharmony_ci netdev_features_t features) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 150162306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 150262306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 150362306a36Sopenharmony_ci .cmd.lif_setattr = { 150462306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 150562306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 150662306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_FEATURES, 150762306a36Sopenharmony_ci }, 150862306a36Sopenharmony_ci }; 150962306a36Sopenharmony_ci u64 vlan_flags = IONIC_ETH_HW_VLAN_TX_TAG | 151062306a36Sopenharmony_ci IONIC_ETH_HW_VLAN_RX_STRIP | 151162306a36Sopenharmony_ci IONIC_ETH_HW_VLAN_RX_FILTER; 151262306a36Sopenharmony_ci u64 old_hw_features; 151362306a36Sopenharmony_ci int err; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (lif->phc) 151862306a36Sopenharmony_ci ctx.cmd.lif_setattr.features |= cpu_to_le64(IONIC_ETH_HW_TIMESTAMP); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 152162306a36Sopenharmony_ci if (err) 152262306a36Sopenharmony_ci return err; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci old_hw_features = lif->hw_features; 152562306a36Sopenharmony_ci lif->hw_features = le64_to_cpu(ctx.cmd.lif_setattr.features & 152662306a36Sopenharmony_ci ctx.comp.lif_setattr.features); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if ((old_hw_features ^ lif->hw_features) & IONIC_ETH_HW_RX_HASH) 152962306a36Sopenharmony_ci ionic_lif_rss_config(lif, lif->rss_types, NULL, NULL); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if ((vlan_flags & le64_to_cpu(ctx.cmd.lif_setattr.features)) && 153262306a36Sopenharmony_ci !(vlan_flags & le64_to_cpu(ctx.comp.lif_setattr.features))) 153362306a36Sopenharmony_ci dev_info_once(lif->ionic->dev, "NIC is not supporting vlan offload, likely in SmartNIC mode\n"); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_TX_TAG) 153662306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_VLAN_TX_TAG\n"); 153762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_STRIP) 153862306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_VLAN_RX_STRIP\n"); 153962306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_FILTER) 154062306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_VLAN_RX_FILTER\n"); 154162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_RX_HASH) 154262306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_RX_HASH\n"); 154362306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TX_SG) 154462306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TX_SG\n"); 154562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TX_CSUM) 154662306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TX_CSUM\n"); 154762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_RX_CSUM) 154862306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_RX_CSUM\n"); 154962306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO) 155062306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO\n"); 155162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPV6) 155262306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_IPV6\n"); 155362306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_ECN) 155462306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_ECN\n"); 155562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_GRE) 155662306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_GRE\n"); 155762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_GRE_CSUM) 155862306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_GRE_CSUM\n"); 155962306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP4) 156062306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_IPXIP4\n"); 156162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP6) 156262306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_IPXIP6\n"); 156362306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_UDP) 156462306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_UDP\n"); 156562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_UDP_CSUM) 156662306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n"); 156762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TIMESTAMP) 156862306a36Sopenharmony_ci dev_dbg(dev, "feature ETH_HW_TIMESTAMP\n"); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci return 0; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int ionic_init_nic_features(struct ionic_lif *lif) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct net_device *netdev = lif->netdev; 157662306a36Sopenharmony_ci netdev_features_t features; 157762306a36Sopenharmony_ci int err; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci /* set up what we expect to support by default */ 158062306a36Sopenharmony_ci features = NETIF_F_HW_VLAN_CTAG_TX | 158162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | 158262306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER | 158362306a36Sopenharmony_ci NETIF_F_SG | 158462306a36Sopenharmony_ci NETIF_F_HW_CSUM | 158562306a36Sopenharmony_ci NETIF_F_RXCSUM | 158662306a36Sopenharmony_ci NETIF_F_TSO | 158762306a36Sopenharmony_ci NETIF_F_TSO6 | 158862306a36Sopenharmony_ci NETIF_F_TSO_ECN | 158962306a36Sopenharmony_ci NETIF_F_GSO_GRE | 159062306a36Sopenharmony_ci NETIF_F_GSO_GRE_CSUM | 159162306a36Sopenharmony_ci NETIF_F_GSO_IPXIP4 | 159262306a36Sopenharmony_ci NETIF_F_GSO_IPXIP6 | 159362306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL | 159462306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci if (lif->nxqs > 1) 159762306a36Sopenharmony_ci features |= NETIF_F_RXHASH; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci err = ionic_set_nic_features(lif, features); 160062306a36Sopenharmony_ci if (err) 160162306a36Sopenharmony_ci return err; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* tell the netdev what we actually can support */ 160462306a36Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_TX_TAG) 160762306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; 160862306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_STRIP) 160962306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; 161062306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_FILTER) 161162306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; 161262306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_RX_HASH) 161362306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 161462306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TX_SG) 161562306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_SG; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TX_CSUM) 161862306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_HW_CSUM; 161962306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_RX_CSUM) 162062306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_RXCSUM; 162162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO) 162262306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_TSO; 162362306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPV6) 162462306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_TSO6; 162562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_ECN) 162662306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_TSO_ECN; 162762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_GRE) 162862306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_GRE; 162962306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_GRE_CSUM) 163062306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_GRE_CSUM; 163162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP4) 163262306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_IPXIP4; 163362306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP6) 163462306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_IPXIP6; 163562306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_UDP) 163662306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL; 163762306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_TSO_UDP_CSUM) 163862306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci netdev->hw_features |= netdev->hw_enc_features; 164162306a36Sopenharmony_ci netdev->features |= netdev->hw_features; 164262306a36Sopenharmony_ci netdev->vlan_features |= netdev->features & ~NETIF_F_VLAN_FEATURES; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci netdev->priv_flags |= IFF_UNICAST_FLT | 164562306a36Sopenharmony_ci IFF_LIVE_ADDR_CHANGE; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci return 0; 164862306a36Sopenharmony_ci} 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cistatic int ionic_set_features(struct net_device *netdev, 165162306a36Sopenharmony_ci netdev_features_t features) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 165462306a36Sopenharmony_ci int err; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci netdev_dbg(netdev, "%s: lif->features=0x%08llx new_features=0x%08llx\n", 165762306a36Sopenharmony_ci __func__, (u64)lif->netdev->features, (u64)features); 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci err = ionic_set_nic_features(lif, features); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci return err; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic int ionic_set_attr_mac(struct ionic_lif *lif, u8 *mac) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 166762306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 166862306a36Sopenharmony_ci .cmd.lif_setattr = { 166962306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 167062306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 167162306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_MAC, 167262306a36Sopenharmony_ci }, 167362306a36Sopenharmony_ci }; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci ether_addr_copy(ctx.cmd.lif_setattr.mac, mac); 167662306a36Sopenharmony_ci return ionic_adminq_post_wait(lif, &ctx); 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int ionic_get_attr_mac(struct ionic_lif *lif, u8 *mac_addr) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 168262306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 168362306a36Sopenharmony_ci .cmd.lif_getattr = { 168462306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_GETATTR, 168562306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 168662306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_MAC, 168762306a36Sopenharmony_ci }, 168862306a36Sopenharmony_ci }; 168962306a36Sopenharmony_ci int err; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 169262306a36Sopenharmony_ci if (err) 169362306a36Sopenharmony_ci return err; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci ether_addr_copy(mac_addr, ctx.comp.lif_getattr.mac); 169662306a36Sopenharmony_ci return 0; 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_cistatic int ionic_program_mac(struct ionic_lif *lif, u8 *mac) 170062306a36Sopenharmony_ci{ 170162306a36Sopenharmony_ci u8 get_mac[ETH_ALEN]; 170262306a36Sopenharmony_ci int err; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci err = ionic_set_attr_mac(lif, mac); 170562306a36Sopenharmony_ci if (err) 170662306a36Sopenharmony_ci return err; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci err = ionic_get_attr_mac(lif, get_mac); 170962306a36Sopenharmony_ci if (err) 171062306a36Sopenharmony_ci return err; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* To deal with older firmware that silently ignores the set attr mac: 171362306a36Sopenharmony_ci * doesn't actually change the mac and doesn't return an error, so we 171462306a36Sopenharmony_ci * do the get attr to verify whether or not the set actually happened 171562306a36Sopenharmony_ci */ 171662306a36Sopenharmony_ci if (!ether_addr_equal(get_mac, mac)) 171762306a36Sopenharmony_ci return 1; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci return 0; 172062306a36Sopenharmony_ci} 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_cistatic int ionic_set_mac_address(struct net_device *netdev, void *sa) 172362306a36Sopenharmony_ci{ 172462306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 172562306a36Sopenharmony_ci struct sockaddr *addr = sa; 172662306a36Sopenharmony_ci u8 *mac; 172762306a36Sopenharmony_ci int err; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci mac = (u8 *)addr->sa_data; 173062306a36Sopenharmony_ci if (ether_addr_equal(netdev->dev_addr, mac)) 173162306a36Sopenharmony_ci return 0; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci err = ionic_program_mac(lif, mac); 173462306a36Sopenharmony_ci if (err < 0) 173562306a36Sopenharmony_ci return err; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (err > 0) 173862306a36Sopenharmony_ci netdev_dbg(netdev, "%s: SET and GET ATTR Mac are not equal-due to old FW running\n", 173962306a36Sopenharmony_ci __func__); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci err = eth_prepare_mac_addr_change(netdev, addr); 174262306a36Sopenharmony_ci if (err) 174362306a36Sopenharmony_ci return err; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci if (!is_zero_ether_addr(netdev->dev_addr)) { 174662306a36Sopenharmony_ci netdev_info(netdev, "deleting mac addr %pM\n", 174762306a36Sopenharmony_ci netdev->dev_addr); 174862306a36Sopenharmony_ci ionic_lif_addr_del(netdev_priv(netdev), netdev->dev_addr); 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci eth_commit_mac_addr_change(netdev, addr); 175262306a36Sopenharmony_ci netdev_info(netdev, "updating mac addr %pM\n", mac); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci return ionic_lif_addr_add(netdev_priv(netdev), mac); 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_civoid ionic_stop_queues_reconfig(struct ionic_lif *lif) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci /* Stop and clean the queues before reconfiguration */ 176062306a36Sopenharmony_ci netif_device_detach(lif->netdev); 176162306a36Sopenharmony_ci ionic_stop_queues(lif); 176262306a36Sopenharmony_ci ionic_txrx_deinit(lif); 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic int ionic_start_queues_reconfig(struct ionic_lif *lif) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci int err; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* Re-init the queues after reconfiguration */ 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci /* The only way txrx_init can fail here is if communication 177262306a36Sopenharmony_ci * with FW is suddenly broken. There's not much we can do 177362306a36Sopenharmony_ci * at this point - error messages have already been printed, 177462306a36Sopenharmony_ci * so we can continue on and the user can eventually do a 177562306a36Sopenharmony_ci * DOWN and UP to try to reset and clear the issue. 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci err = ionic_txrx_init(lif); 177862306a36Sopenharmony_ci ionic_link_status_check_request(lif, CAN_NOT_SLEEP); 177962306a36Sopenharmony_ci netif_device_attach(lif->netdev); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci return err; 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic int ionic_change_mtu(struct net_device *netdev, int new_mtu) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 178762306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 178862306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 178962306a36Sopenharmony_ci .cmd.lif_setattr = { 179062306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 179162306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 179262306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_MTU, 179362306a36Sopenharmony_ci .mtu = cpu_to_le32(new_mtu), 179462306a36Sopenharmony_ci }, 179562306a36Sopenharmony_ci }; 179662306a36Sopenharmony_ci int err; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 179962306a36Sopenharmony_ci if (err) 180062306a36Sopenharmony_ci return err; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci /* if we're not running, nothing more to do */ 180362306a36Sopenharmony_ci if (!netif_running(netdev)) { 180462306a36Sopenharmony_ci netdev->mtu = new_mtu; 180562306a36Sopenharmony_ci return 0; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 180962306a36Sopenharmony_ci ionic_stop_queues_reconfig(lif); 181062306a36Sopenharmony_ci netdev->mtu = new_mtu; 181162306a36Sopenharmony_ci err = ionic_start_queues_reconfig(lif); 181262306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci return err; 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_cistatic void ionic_tx_timeout_work(struct work_struct *ws) 181862306a36Sopenharmony_ci{ 181962306a36Sopenharmony_ci struct ionic_lif *lif = container_of(ws, struct ionic_lif, tx_timeout_work); 182062306a36Sopenharmony_ci int err; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) 182362306a36Sopenharmony_ci return; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci /* if we were stopped before this scheduled job was launched, 182662306a36Sopenharmony_ci * don't bother the queues as they are already stopped. 182762306a36Sopenharmony_ci */ 182862306a36Sopenharmony_ci if (!netif_running(lif->netdev)) 182962306a36Sopenharmony_ci return; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 183262306a36Sopenharmony_ci ionic_stop_queues_reconfig(lif); 183362306a36Sopenharmony_ci err = ionic_start_queues_reconfig(lif); 183462306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (err) 183762306a36Sopenharmony_ci dev_err(lif->ionic->dev, "%s: Restarting queues failed\n", __func__); 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic void ionic_tx_timeout(struct net_device *netdev, unsigned int txqueue) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci netdev_info(lif->netdev, "Tx Timeout triggered - txq %d\n", txqueue); 184562306a36Sopenharmony_ci schedule_work(&lif->tx_timeout_work); 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, 184962306a36Sopenharmony_ci u16 vid) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 185262306a36Sopenharmony_ci int err; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci err = ionic_lif_vlan_add(lif, vid); 185562306a36Sopenharmony_ci if (err) 185662306a36Sopenharmony_ci return err; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci ionic_lif_rx_mode(lif); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci return 0; 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, 186462306a36Sopenharmony_ci u16 vid) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 186762306a36Sopenharmony_ci int err; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci err = ionic_lif_vlan_del(lif, vid); 187062306a36Sopenharmony_ci if (err) 187162306a36Sopenharmony_ci return err; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci ionic_lif_rx_mode(lif); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci return 0; 187662306a36Sopenharmony_ci} 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ciint ionic_lif_rss_config(struct ionic_lif *lif, const u16 types, 187962306a36Sopenharmony_ci const u8 *key, const u32 *indir) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 188262306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 188362306a36Sopenharmony_ci .cmd.lif_setattr = { 188462306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 188562306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_RSS, 188662306a36Sopenharmony_ci .rss.addr = cpu_to_le64(lif->rss_ind_tbl_pa), 188762306a36Sopenharmony_ci }, 188862306a36Sopenharmony_ci }; 188962306a36Sopenharmony_ci unsigned int i, tbl_sz; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (lif->hw_features & IONIC_ETH_HW_RX_HASH) { 189262306a36Sopenharmony_ci lif->rss_types = types; 189362306a36Sopenharmony_ci ctx.cmd.lif_setattr.rss.types = cpu_to_le16(types); 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (key) 189762306a36Sopenharmony_ci memcpy(lif->rss_hash_key, key, IONIC_RSS_HASH_KEY_SIZE); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci if (indir) { 190062306a36Sopenharmony_ci tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 190162306a36Sopenharmony_ci for (i = 0; i < tbl_sz; i++) 190262306a36Sopenharmony_ci lif->rss_ind_tbl[i] = indir[i]; 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci memcpy(ctx.cmd.lif_setattr.rss.key, lif->rss_hash_key, 190662306a36Sopenharmony_ci IONIC_RSS_HASH_KEY_SIZE); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci return ionic_adminq_post_wait(lif, &ctx); 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cistatic int ionic_lif_rss_init(struct ionic_lif *lif) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci unsigned int tbl_sz; 191462306a36Sopenharmony_ci unsigned int i; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci lif->rss_types = IONIC_RSS_TYPE_IPV4 | 191762306a36Sopenharmony_ci IONIC_RSS_TYPE_IPV4_TCP | 191862306a36Sopenharmony_ci IONIC_RSS_TYPE_IPV4_UDP | 191962306a36Sopenharmony_ci IONIC_RSS_TYPE_IPV6 | 192062306a36Sopenharmony_ci IONIC_RSS_TYPE_IPV6_TCP | 192162306a36Sopenharmony_ci IONIC_RSS_TYPE_IPV6_UDP; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci /* Fill indirection table with 'default' values */ 192462306a36Sopenharmony_ci tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 192562306a36Sopenharmony_ci for (i = 0; i < tbl_sz; i++) 192662306a36Sopenharmony_ci lif->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i, lif->nxqs); 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci return ionic_lif_rss_config(lif, lif->rss_types, NULL, NULL); 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic void ionic_lif_rss_deinit(struct ionic_lif *lif) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci int tbl_sz; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 193662306a36Sopenharmony_ci memset(lif->rss_ind_tbl, 0, tbl_sz); 193762306a36Sopenharmony_ci memset(lif->rss_hash_key, 0, IONIC_RSS_HASH_KEY_SIZE); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci ionic_lif_rss_config(lif, 0x0, NULL, NULL); 194062306a36Sopenharmony_ci} 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_cistatic void ionic_lif_quiesce(struct ionic_lif *lif) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 194562306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 194662306a36Sopenharmony_ci .cmd.lif_setattr = { 194762306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 194862306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 194962306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_STATE, 195062306a36Sopenharmony_ci .state = IONIC_LIF_QUIESCE, 195162306a36Sopenharmony_ci }, 195262306a36Sopenharmony_ci }; 195362306a36Sopenharmony_ci int err; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 195662306a36Sopenharmony_ci if (err) 195762306a36Sopenharmony_ci netdev_dbg(lif->netdev, "lif quiesce failed %d\n", err); 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic void ionic_txrx_disable(struct ionic_lif *lif) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci unsigned int i; 196362306a36Sopenharmony_ci int err = 0; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (lif->txqcqs) { 196662306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) 196762306a36Sopenharmony_ci err = ionic_qcq_disable(lif, lif->txqcqs[i], err); 196862306a36Sopenharmony_ci } 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci if (lif->hwstamp_txq) 197162306a36Sopenharmony_ci err = ionic_qcq_disable(lif, lif->hwstamp_txq, err); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci if (lif->rxqcqs) { 197462306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) 197562306a36Sopenharmony_ci err = ionic_qcq_disable(lif, lif->rxqcqs[i], err); 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (lif->hwstamp_rxq) 197962306a36Sopenharmony_ci err = ionic_qcq_disable(lif, lif->hwstamp_rxq, err); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci ionic_lif_quiesce(lif); 198262306a36Sopenharmony_ci} 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_cistatic void ionic_txrx_deinit(struct ionic_lif *lif) 198562306a36Sopenharmony_ci{ 198662306a36Sopenharmony_ci unsigned int i; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (lif->txqcqs) { 198962306a36Sopenharmony_ci for (i = 0; i < lif->nxqs && lif->txqcqs[i]; i++) { 199062306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->txqcqs[i]); 199162306a36Sopenharmony_ci ionic_tx_flush(&lif->txqcqs[i]->cq); 199262306a36Sopenharmony_ci ionic_tx_empty(&lif->txqcqs[i]->q); 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci if (lif->rxqcqs) { 199762306a36Sopenharmony_ci for (i = 0; i < lif->nxqs && lif->rxqcqs[i]; i++) { 199862306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]); 199962306a36Sopenharmony_ci ionic_rx_empty(&lif->rxqcqs[i]->q); 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci lif->rx_mode = 0; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (lif->hwstamp_txq) { 200562306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->hwstamp_txq); 200662306a36Sopenharmony_ci ionic_tx_flush(&lif->hwstamp_txq->cq); 200762306a36Sopenharmony_ci ionic_tx_empty(&lif->hwstamp_txq->q); 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci if (lif->hwstamp_rxq) { 201162306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->hwstamp_rxq); 201262306a36Sopenharmony_ci ionic_rx_empty(&lif->hwstamp_rxq->q); 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci} 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_civoid ionic_txrx_free(struct ionic_lif *lif) 201762306a36Sopenharmony_ci{ 201862306a36Sopenharmony_ci unsigned int i; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if (lif->txqcqs) { 202162306a36Sopenharmony_ci for (i = 0; i < lif->ionic->ntxqs_per_lif && lif->txqcqs[i]; i++) { 202262306a36Sopenharmony_ci ionic_qcq_free(lif, lif->txqcqs[i]); 202362306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, lif->txqcqs[i]); 202462306a36Sopenharmony_ci lif->txqcqs[i] = NULL; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci if (lif->rxqcqs) { 202962306a36Sopenharmony_ci for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) { 203062306a36Sopenharmony_ci ionic_qcq_free(lif, lif->rxqcqs[i]); 203162306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, lif->rxqcqs[i]); 203262306a36Sopenharmony_ci lif->rxqcqs[i] = NULL; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci if (lif->hwstamp_txq) { 203762306a36Sopenharmony_ci ionic_qcq_free(lif, lif->hwstamp_txq); 203862306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, lif->hwstamp_txq); 203962306a36Sopenharmony_ci lif->hwstamp_txq = NULL; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci if (lif->hwstamp_rxq) { 204362306a36Sopenharmony_ci ionic_qcq_free(lif, lif->hwstamp_rxq); 204462306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, lif->hwstamp_rxq); 204562306a36Sopenharmony_ci lif->hwstamp_rxq = NULL; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci} 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_cistatic int ionic_txrx_alloc(struct ionic_lif *lif) 205062306a36Sopenharmony_ci{ 205162306a36Sopenharmony_ci unsigned int comp_sz, desc_sz, num_desc, sg_desc_sz; 205262306a36Sopenharmony_ci unsigned int flags, i; 205362306a36Sopenharmony_ci int err = 0; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci num_desc = lif->ntxq_descs; 205662306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_txq_desc); 205762306a36Sopenharmony_ci comp_sz = sizeof(struct ionic_txq_comp); 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && 206062306a36Sopenharmony_ci lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == 206162306a36Sopenharmony_ci sizeof(struct ionic_txq_sg_desc_v1)) 206262306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); 206362306a36Sopenharmony_ci else 206462306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state)) 206962306a36Sopenharmony_ci flags |= IONIC_QCQ_F_CMB_RINGS; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 207262306a36Sopenharmony_ci flags |= IONIC_QCQ_F_INTR; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) { 207562306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, 207662306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 207762306a36Sopenharmony_ci lif->kern_pid, &lif->txqcqs[i]); 207862306a36Sopenharmony_ci if (err) 207962306a36Sopenharmony_ci goto err_out; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci if (flags & IONIC_QCQ_F_INTR) { 208262306a36Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 208362306a36Sopenharmony_ci lif->txqcqs[i]->intr.index, 208462306a36Sopenharmony_ci lif->tx_coalesce_hw); 208562306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state)) 208662306a36Sopenharmony_ci lif->txqcqs[i]->intr.dim_coal_hw = lif->tx_coalesce_hw; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->txqcqs[i]); 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG | IONIC_QCQ_F_INTR; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state)) 209562306a36Sopenharmony_ci flags |= IONIC_QCQ_F_CMB_RINGS; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci num_desc = lif->nrxq_descs; 209862306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_rxq_desc); 209962306a36Sopenharmony_ci comp_sz = sizeof(struct ionic_rxq_comp); 210062306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci if (lif->rxq_features & IONIC_Q_F_2X_CQ_DESC) 210362306a36Sopenharmony_ci comp_sz *= 2; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) { 210662306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, 210762306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 210862306a36Sopenharmony_ci lif->kern_pid, &lif->rxqcqs[i]); 210962306a36Sopenharmony_ci if (err) 211062306a36Sopenharmony_ci goto err_out; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci lif->rxqcqs[i]->q.features = lif->rxq_features; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 211562306a36Sopenharmony_ci lif->rxqcqs[i]->intr.index, 211662306a36Sopenharmony_ci lif->rx_coalesce_hw); 211762306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state)) 211862306a36Sopenharmony_ci lif->rxqcqs[i]->intr.dim_coal_hw = lif->rx_coalesce_hw; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 212162306a36Sopenharmony_ci ionic_link_qcq_interrupts(lif->rxqcqs[i], 212262306a36Sopenharmony_ci lif->txqcqs[i]); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->rxqcqs[i]); 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci return 0; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cierr_out: 213062306a36Sopenharmony_ci ionic_txrx_free(lif); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci return err; 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic int ionic_txrx_init(struct ionic_lif *lif) 213662306a36Sopenharmony_ci{ 213762306a36Sopenharmony_ci unsigned int i; 213862306a36Sopenharmony_ci int err; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) { 214162306a36Sopenharmony_ci err = ionic_lif_txq_init(lif, lif->txqcqs[i]); 214262306a36Sopenharmony_ci if (err) 214362306a36Sopenharmony_ci goto err_out; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci err = ionic_lif_rxq_init(lif, lif->rxqcqs[i]); 214662306a36Sopenharmony_ci if (err) { 214762306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->txqcqs[i]); 214862306a36Sopenharmony_ci goto err_out; 214962306a36Sopenharmony_ci } 215062306a36Sopenharmony_ci } 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (lif->netdev->features & NETIF_F_RXHASH) 215362306a36Sopenharmony_ci ionic_lif_rss_init(lif); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci ionic_lif_rx_mode(lif); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci return 0; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_cierr_out: 216062306a36Sopenharmony_ci while (i--) { 216162306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->txqcqs[i]); 216262306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]); 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci return err; 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cistatic int ionic_txrx_enable(struct ionic_lif *lif) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci int derr = 0; 217162306a36Sopenharmony_ci int i, err; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) { 217462306a36Sopenharmony_ci if (!(lif->rxqcqs[i] && lif->txqcqs[i])) { 217562306a36Sopenharmony_ci dev_err(lif->ionic->dev, "%s: bad qcq %d\n", __func__, i); 217662306a36Sopenharmony_ci err = -ENXIO; 217762306a36Sopenharmony_ci goto err_out; 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci ionic_rx_fill(&lif->rxqcqs[i]->q); 218162306a36Sopenharmony_ci err = ionic_qcq_enable(lif->rxqcqs[i]); 218262306a36Sopenharmony_ci if (err) 218362306a36Sopenharmony_ci goto err_out; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci err = ionic_qcq_enable(lif->txqcqs[i]); 218662306a36Sopenharmony_ci if (err) { 218762306a36Sopenharmony_ci derr = ionic_qcq_disable(lif, lif->rxqcqs[i], err); 218862306a36Sopenharmony_ci goto err_out; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci if (lif->hwstamp_rxq) { 219362306a36Sopenharmony_ci ionic_rx_fill(&lif->hwstamp_rxq->q); 219462306a36Sopenharmony_ci err = ionic_qcq_enable(lif->hwstamp_rxq); 219562306a36Sopenharmony_ci if (err) 219662306a36Sopenharmony_ci goto err_out_hwstamp_rx; 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci if (lif->hwstamp_txq) { 220062306a36Sopenharmony_ci err = ionic_qcq_enable(lif->hwstamp_txq); 220162306a36Sopenharmony_ci if (err) 220262306a36Sopenharmony_ci goto err_out_hwstamp_tx; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci return 0; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_cierr_out_hwstamp_tx: 220862306a36Sopenharmony_ci if (lif->hwstamp_rxq) 220962306a36Sopenharmony_ci derr = ionic_qcq_disable(lif, lif->hwstamp_rxq, derr); 221062306a36Sopenharmony_cierr_out_hwstamp_rx: 221162306a36Sopenharmony_ci i = lif->nxqs; 221262306a36Sopenharmony_cierr_out: 221362306a36Sopenharmony_ci while (i--) { 221462306a36Sopenharmony_ci derr = ionic_qcq_disable(lif, lif->txqcqs[i], derr); 221562306a36Sopenharmony_ci derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr); 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci return err; 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cistatic int ionic_start_queues(struct ionic_lif *lif) 222262306a36Sopenharmony_ci{ 222362306a36Sopenharmony_ci int err; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_BROKEN, lif->state)) 222662306a36Sopenharmony_ci return -EIO; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) 222962306a36Sopenharmony_ci return -EBUSY; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (test_and_set_bit(IONIC_LIF_F_UP, lif->state)) 223262306a36Sopenharmony_ci return 0; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci err = ionic_txrx_enable(lif); 223562306a36Sopenharmony_ci if (err) { 223662306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_UP, lif->state); 223762306a36Sopenharmony_ci return err; 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci netif_tx_wake_all_queues(lif->netdev); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci return 0; 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_cistatic int ionic_open(struct net_device *netdev) 224562306a36Sopenharmony_ci{ 224662306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 224762306a36Sopenharmony_ci int err; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* If recovering from a broken state, clear the bit and we'll try again */ 225062306a36Sopenharmony_ci if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state)) 225162306a36Sopenharmony_ci netdev_info(netdev, "clearing broken state\n"); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci err = ionic_txrx_alloc(lif); 225662306a36Sopenharmony_ci if (err) 225762306a36Sopenharmony_ci goto err_unlock; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci err = ionic_txrx_init(lif); 226062306a36Sopenharmony_ci if (err) 226162306a36Sopenharmony_ci goto err_txrx_free; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci err = netif_set_real_num_tx_queues(netdev, lif->nxqs); 226462306a36Sopenharmony_ci if (err) 226562306a36Sopenharmony_ci goto err_txrx_deinit; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci err = netif_set_real_num_rx_queues(netdev, lif->nxqs); 226862306a36Sopenharmony_ci if (err) 226962306a36Sopenharmony_ci goto err_txrx_deinit; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci /* don't start the queues until we have link */ 227262306a36Sopenharmony_ci if (netif_carrier_ok(netdev)) { 227362306a36Sopenharmony_ci err = ionic_start_queues(lif); 227462306a36Sopenharmony_ci if (err) 227562306a36Sopenharmony_ci goto err_txrx_deinit; 227662306a36Sopenharmony_ci } 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci /* If hardware timestamping is enabled, but the queues were freed by 227962306a36Sopenharmony_ci * ionic_stop, those need to be reallocated and initialized, too. 228062306a36Sopenharmony_ci */ 228162306a36Sopenharmony_ci ionic_lif_hwstamp_recreate_queues(lif); 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci return 0; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cierr_txrx_deinit: 228862306a36Sopenharmony_ci ionic_txrx_deinit(lif); 228962306a36Sopenharmony_cierr_txrx_free: 229062306a36Sopenharmony_ci ionic_txrx_free(lif); 229162306a36Sopenharmony_cierr_unlock: 229262306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 229362306a36Sopenharmony_ci return err; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cistatic void ionic_stop_queues(struct ionic_lif *lif) 229762306a36Sopenharmony_ci{ 229862306a36Sopenharmony_ci if (!test_and_clear_bit(IONIC_LIF_F_UP, lif->state)) 229962306a36Sopenharmony_ci return; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci netif_tx_disable(lif->netdev); 230262306a36Sopenharmony_ci ionic_txrx_disable(lif); 230362306a36Sopenharmony_ci} 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_cistatic int ionic_stop(struct net_device *netdev) 230662306a36Sopenharmony_ci{ 230762306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) 231062306a36Sopenharmony_ci return 0; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 231362306a36Sopenharmony_ci ionic_stop_queues(lif); 231462306a36Sopenharmony_ci ionic_txrx_deinit(lif); 231562306a36Sopenharmony_ci ionic_txrx_free(lif); 231662306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci return 0; 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci switch (cmd) { 232662306a36Sopenharmony_ci case SIOCSHWTSTAMP: 232762306a36Sopenharmony_ci return ionic_lif_hwstamp_set(lif, ifr); 232862306a36Sopenharmony_ci case SIOCGHWTSTAMP: 232962306a36Sopenharmony_ci return ionic_lif_hwstamp_get(lif, ifr); 233062306a36Sopenharmony_ci default: 233162306a36Sopenharmony_ci return -EOPNOTSUPP; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci} 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cistatic int ionic_get_fw_vf_config(struct ionic *ionic, int vf, struct ionic_vf *vfdata) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct ionic_vf_getattr_comp comp = { 0 }; 233862306a36Sopenharmony_ci int err; 233962306a36Sopenharmony_ci u8 attr; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci attr = IONIC_VF_ATTR_VLAN; 234262306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 234362306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 234462306a36Sopenharmony_ci goto err_out; 234562306a36Sopenharmony_ci if (!err) 234662306a36Sopenharmony_ci vfdata->vlanid = comp.vlanid; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci attr = IONIC_VF_ATTR_SPOOFCHK; 234962306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 235062306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 235162306a36Sopenharmony_ci goto err_out; 235262306a36Sopenharmony_ci if (!err) 235362306a36Sopenharmony_ci vfdata->spoofchk = comp.spoofchk; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci attr = IONIC_VF_ATTR_LINKSTATE; 235662306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 235762306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 235862306a36Sopenharmony_ci goto err_out; 235962306a36Sopenharmony_ci if (!err) { 236062306a36Sopenharmony_ci switch (comp.linkstate) { 236162306a36Sopenharmony_ci case IONIC_VF_LINK_STATUS_UP: 236262306a36Sopenharmony_ci vfdata->linkstate = IFLA_VF_LINK_STATE_ENABLE; 236362306a36Sopenharmony_ci break; 236462306a36Sopenharmony_ci case IONIC_VF_LINK_STATUS_DOWN: 236562306a36Sopenharmony_ci vfdata->linkstate = IFLA_VF_LINK_STATE_DISABLE; 236662306a36Sopenharmony_ci break; 236762306a36Sopenharmony_ci case IONIC_VF_LINK_STATUS_AUTO: 236862306a36Sopenharmony_ci vfdata->linkstate = IFLA_VF_LINK_STATE_AUTO; 236962306a36Sopenharmony_ci break; 237062306a36Sopenharmony_ci default: 237162306a36Sopenharmony_ci dev_warn(ionic->dev, "Unexpected link state %u\n", comp.linkstate); 237262306a36Sopenharmony_ci break; 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci attr = IONIC_VF_ATTR_RATE; 237762306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 237862306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 237962306a36Sopenharmony_ci goto err_out; 238062306a36Sopenharmony_ci if (!err) 238162306a36Sopenharmony_ci vfdata->maxrate = comp.maxrate; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci attr = IONIC_VF_ATTR_TRUST; 238462306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 238562306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 238662306a36Sopenharmony_ci goto err_out; 238762306a36Sopenharmony_ci if (!err) 238862306a36Sopenharmony_ci vfdata->trusted = comp.trust; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci attr = IONIC_VF_ATTR_MAC; 239162306a36Sopenharmony_ci err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); 239262306a36Sopenharmony_ci if (err && comp.status != IONIC_RC_ENOSUPP) 239362306a36Sopenharmony_ci goto err_out; 239462306a36Sopenharmony_ci if (!err) 239562306a36Sopenharmony_ci ether_addr_copy(vfdata->macaddr, comp.macaddr); 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cierr_out: 239862306a36Sopenharmony_ci if (err) 239962306a36Sopenharmony_ci dev_err(ionic->dev, "Failed to get %s for VF %d\n", 240062306a36Sopenharmony_ci ionic_vf_attr_to_str(attr), vf); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci return err; 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic int ionic_get_vf_config(struct net_device *netdev, 240662306a36Sopenharmony_ci int vf, struct ifla_vf_info *ivf) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 240962306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 241062306a36Sopenharmony_ci struct ionic_vf vfdata = { 0 }; 241162306a36Sopenharmony_ci int ret = 0; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci if (!netif_device_present(netdev)) 241462306a36Sopenharmony_ci return -EBUSY; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci down_read(&ionic->vf_op_lock); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 241962306a36Sopenharmony_ci ret = -EINVAL; 242062306a36Sopenharmony_ci } else { 242162306a36Sopenharmony_ci ivf->vf = vf; 242262306a36Sopenharmony_ci ivf->qos = 0; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci ret = ionic_get_fw_vf_config(ionic, vf, &vfdata); 242562306a36Sopenharmony_ci if (!ret) { 242662306a36Sopenharmony_ci ivf->vlan = le16_to_cpu(vfdata.vlanid); 242762306a36Sopenharmony_ci ivf->spoofchk = vfdata.spoofchk; 242862306a36Sopenharmony_ci ivf->linkstate = vfdata.linkstate; 242962306a36Sopenharmony_ci ivf->max_tx_rate = le32_to_cpu(vfdata.maxrate); 243062306a36Sopenharmony_ci ivf->trusted = vfdata.trusted; 243162306a36Sopenharmony_ci ether_addr_copy(ivf->mac, vfdata.macaddr); 243262306a36Sopenharmony_ci } 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci up_read(&ionic->vf_op_lock); 243662306a36Sopenharmony_ci return ret; 243762306a36Sopenharmony_ci} 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_cistatic int ionic_get_vf_stats(struct net_device *netdev, int vf, 244062306a36Sopenharmony_ci struct ifla_vf_stats *vf_stats) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 244362306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 244462306a36Sopenharmony_ci struct ionic_lif_stats *vs; 244562306a36Sopenharmony_ci int ret = 0; 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci if (!netif_device_present(netdev)) 244862306a36Sopenharmony_ci return -EBUSY; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci down_read(&ionic->vf_op_lock); 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 245362306a36Sopenharmony_ci ret = -EINVAL; 245462306a36Sopenharmony_ci } else { 245562306a36Sopenharmony_ci memset(vf_stats, 0, sizeof(*vf_stats)); 245662306a36Sopenharmony_ci vs = &ionic->vfs[vf].stats; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci vf_stats->rx_packets = le64_to_cpu(vs->rx_ucast_packets); 245962306a36Sopenharmony_ci vf_stats->tx_packets = le64_to_cpu(vs->tx_ucast_packets); 246062306a36Sopenharmony_ci vf_stats->rx_bytes = le64_to_cpu(vs->rx_ucast_bytes); 246162306a36Sopenharmony_ci vf_stats->tx_bytes = le64_to_cpu(vs->tx_ucast_bytes); 246262306a36Sopenharmony_ci vf_stats->broadcast = le64_to_cpu(vs->rx_bcast_packets); 246362306a36Sopenharmony_ci vf_stats->multicast = le64_to_cpu(vs->rx_mcast_packets); 246462306a36Sopenharmony_ci vf_stats->rx_dropped = le64_to_cpu(vs->rx_ucast_drop_packets) + 246562306a36Sopenharmony_ci le64_to_cpu(vs->rx_mcast_drop_packets) + 246662306a36Sopenharmony_ci le64_to_cpu(vs->rx_bcast_drop_packets); 246762306a36Sopenharmony_ci vf_stats->tx_dropped = le64_to_cpu(vs->tx_ucast_drop_packets) + 246862306a36Sopenharmony_ci le64_to_cpu(vs->tx_mcast_drop_packets) + 246962306a36Sopenharmony_ci le64_to_cpu(vs->tx_bcast_drop_packets); 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci up_read(&ionic->vf_op_lock); 247362306a36Sopenharmony_ci return ret; 247462306a36Sopenharmony_ci} 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_cistatic int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) 247762306a36Sopenharmony_ci{ 247862306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_MAC }; 247962306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 248062306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 248162306a36Sopenharmony_ci int ret; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci if (!(is_zero_ether_addr(mac) || is_valid_ether_addr(mac))) 248462306a36Sopenharmony_ci return -EINVAL; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci if (!netif_device_present(netdev)) 248762306a36Sopenharmony_ci return -EBUSY; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 249262306a36Sopenharmony_ci ret = -EINVAL; 249362306a36Sopenharmony_ci } else { 249462306a36Sopenharmony_ci ether_addr_copy(vfc.macaddr, mac); 249562306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n", 249662306a36Sopenharmony_ci __func__, vf, vfc.macaddr); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 249962306a36Sopenharmony_ci if (!ret) 250062306a36Sopenharmony_ci ether_addr_copy(ionic->vfs[vf].macaddr, mac); 250162306a36Sopenharmony_ci } 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 250462306a36Sopenharmony_ci return ret; 250562306a36Sopenharmony_ci} 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_cistatic int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, 250862306a36Sopenharmony_ci u8 qos, __be16 proto) 250962306a36Sopenharmony_ci{ 251062306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_VLAN }; 251162306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 251262306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 251362306a36Sopenharmony_ci int ret; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci /* until someday when we support qos */ 251662306a36Sopenharmony_ci if (qos) 251762306a36Sopenharmony_ci return -EINVAL; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (vlan > 4095) 252062306a36Sopenharmony_ci return -EINVAL; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci if (proto != htons(ETH_P_8021Q)) 252362306a36Sopenharmony_ci return -EPROTONOSUPPORT; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci if (!netif_device_present(netdev)) 252662306a36Sopenharmony_ci return -EBUSY; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 253162306a36Sopenharmony_ci ret = -EINVAL; 253262306a36Sopenharmony_ci } else { 253362306a36Sopenharmony_ci vfc.vlanid = cpu_to_le16(vlan); 253462306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d vlan %d\n", 253562306a36Sopenharmony_ci __func__, vf, le16_to_cpu(vfc.vlanid)); 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 253862306a36Sopenharmony_ci if (!ret) 253962306a36Sopenharmony_ci ionic->vfs[vf].vlanid = cpu_to_le16(vlan); 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 254362306a36Sopenharmony_ci return ret; 254462306a36Sopenharmony_ci} 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_cistatic int ionic_set_vf_rate(struct net_device *netdev, int vf, 254762306a36Sopenharmony_ci int tx_min, int tx_max) 254862306a36Sopenharmony_ci{ 254962306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_RATE }; 255062306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 255162306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 255262306a36Sopenharmony_ci int ret; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci /* setting the min just seems silly */ 255562306a36Sopenharmony_ci if (tx_min) 255662306a36Sopenharmony_ci return -EINVAL; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci if (!netif_device_present(netdev)) 255962306a36Sopenharmony_ci return -EBUSY; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 256462306a36Sopenharmony_ci ret = -EINVAL; 256562306a36Sopenharmony_ci } else { 256662306a36Sopenharmony_ci vfc.maxrate = cpu_to_le32(tx_max); 256762306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n", 256862306a36Sopenharmony_ci __func__, vf, le32_to_cpu(vfc.maxrate)); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 257162306a36Sopenharmony_ci if (!ret) 257262306a36Sopenharmony_ci ionic->vfs[vf].maxrate = cpu_to_le32(tx_max); 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 257662306a36Sopenharmony_ci return ret; 257762306a36Sopenharmony_ci} 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_cistatic int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) 258062306a36Sopenharmony_ci{ 258162306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_SPOOFCHK }; 258262306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 258362306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 258462306a36Sopenharmony_ci int ret; 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci if (!netif_device_present(netdev)) 258762306a36Sopenharmony_ci return -EBUSY; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 259262306a36Sopenharmony_ci ret = -EINVAL; 259362306a36Sopenharmony_ci } else { 259462306a36Sopenharmony_ci vfc.spoofchk = set; 259562306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d spoof %d\n", 259662306a36Sopenharmony_ci __func__, vf, vfc.spoofchk); 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 259962306a36Sopenharmony_ci if (!ret) 260062306a36Sopenharmony_ci ionic->vfs[vf].spoofchk = set; 260162306a36Sopenharmony_ci } 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 260462306a36Sopenharmony_ci return ret; 260562306a36Sopenharmony_ci} 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_cistatic int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_TRUST }; 261062306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 261162306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 261262306a36Sopenharmony_ci int ret; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci if (!netif_device_present(netdev)) 261562306a36Sopenharmony_ci return -EBUSY; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 262062306a36Sopenharmony_ci ret = -EINVAL; 262162306a36Sopenharmony_ci } else { 262262306a36Sopenharmony_ci vfc.trust = set; 262362306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d trust %d\n", 262462306a36Sopenharmony_ci __func__, vf, vfc.trust); 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 262762306a36Sopenharmony_ci if (!ret) 262862306a36Sopenharmony_ci ionic->vfs[vf].trusted = set; 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 263262306a36Sopenharmony_ci return ret; 263362306a36Sopenharmony_ci} 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_cistatic int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_LINKSTATE }; 263862306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 263962306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 264062306a36Sopenharmony_ci u8 vfls; 264162306a36Sopenharmony_ci int ret; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci switch (set) { 264462306a36Sopenharmony_ci case IFLA_VF_LINK_STATE_ENABLE: 264562306a36Sopenharmony_ci vfls = IONIC_VF_LINK_STATUS_UP; 264662306a36Sopenharmony_ci break; 264762306a36Sopenharmony_ci case IFLA_VF_LINK_STATE_DISABLE: 264862306a36Sopenharmony_ci vfls = IONIC_VF_LINK_STATUS_DOWN; 264962306a36Sopenharmony_ci break; 265062306a36Sopenharmony_ci case IFLA_VF_LINK_STATE_AUTO: 265162306a36Sopenharmony_ci vfls = IONIC_VF_LINK_STATUS_AUTO; 265262306a36Sopenharmony_ci break; 265362306a36Sopenharmony_ci default: 265462306a36Sopenharmony_ci return -EINVAL; 265562306a36Sopenharmony_ci } 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci if (!netif_device_present(netdev)) 265862306a36Sopenharmony_ci return -EBUSY; 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci down_write(&ionic->vf_op_lock); 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { 266362306a36Sopenharmony_ci ret = -EINVAL; 266462306a36Sopenharmony_ci } else { 266562306a36Sopenharmony_ci vfc.linkstate = vfls; 266662306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n", 266762306a36Sopenharmony_ci __func__, vf, vfc.linkstate); 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci ret = ionic_set_vf_config(ionic, vf, &vfc); 267062306a36Sopenharmony_ci if (!ret) 267162306a36Sopenharmony_ci ionic->vfs[vf].linkstate = set; 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci up_write(&ionic->vf_op_lock); 267562306a36Sopenharmony_ci return ret; 267662306a36Sopenharmony_ci} 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_cistatic void ionic_vf_attr_replay(struct ionic_lif *lif) 267962306a36Sopenharmony_ci{ 268062306a36Sopenharmony_ci struct ionic_vf_setattr_cmd vfc = { }; 268162306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 268262306a36Sopenharmony_ci struct ionic_vf *v; 268362306a36Sopenharmony_ci int i; 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci if (!ionic->vfs) 268662306a36Sopenharmony_ci return; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci down_read(&ionic->vf_op_lock); 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci for (i = 0; i < ionic->num_vfs; i++) { 269162306a36Sopenharmony_ci v = &ionic->vfs[i]; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (v->stats_pa) { 269462306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_STATSADDR; 269562306a36Sopenharmony_ci vfc.stats_pa = cpu_to_le64(v->stats_pa); 269662306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 269762306a36Sopenharmony_ci vfc.stats_pa = 0; 269862306a36Sopenharmony_ci } 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci if (!is_zero_ether_addr(v->macaddr)) { 270162306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_MAC; 270262306a36Sopenharmony_ci ether_addr_copy(vfc.macaddr, v->macaddr); 270362306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 270462306a36Sopenharmony_ci eth_zero_addr(vfc.macaddr); 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci if (v->vlanid) { 270862306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_VLAN; 270962306a36Sopenharmony_ci vfc.vlanid = v->vlanid; 271062306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 271162306a36Sopenharmony_ci vfc.vlanid = 0; 271262306a36Sopenharmony_ci } 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci if (v->maxrate) { 271562306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_RATE; 271662306a36Sopenharmony_ci vfc.maxrate = v->maxrate; 271762306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 271862306a36Sopenharmony_ci vfc.maxrate = 0; 271962306a36Sopenharmony_ci } 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci if (v->spoofchk) { 272262306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_SPOOFCHK; 272362306a36Sopenharmony_ci vfc.spoofchk = v->spoofchk; 272462306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 272562306a36Sopenharmony_ci vfc.spoofchk = 0; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci if (v->trusted) { 272962306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_TRUST; 273062306a36Sopenharmony_ci vfc.trust = v->trusted; 273162306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 273262306a36Sopenharmony_ci vfc.trust = 0; 273362306a36Sopenharmony_ci } 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci if (v->linkstate) { 273662306a36Sopenharmony_ci vfc.attr = IONIC_VF_ATTR_LINKSTATE; 273762306a36Sopenharmony_ci vfc.linkstate = v->linkstate; 273862306a36Sopenharmony_ci ionic_set_vf_config(ionic, i, &vfc); 273962306a36Sopenharmony_ci vfc.linkstate = 0; 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci } 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci up_read(&ionic->vf_op_lock); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci ionic_vf_start(ionic); 274662306a36Sopenharmony_ci} 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_cistatic const struct net_device_ops ionic_netdev_ops = { 274962306a36Sopenharmony_ci .ndo_open = ionic_open, 275062306a36Sopenharmony_ci .ndo_stop = ionic_stop, 275162306a36Sopenharmony_ci .ndo_eth_ioctl = ionic_eth_ioctl, 275262306a36Sopenharmony_ci .ndo_start_xmit = ionic_start_xmit, 275362306a36Sopenharmony_ci .ndo_get_stats64 = ionic_get_stats64, 275462306a36Sopenharmony_ci .ndo_set_rx_mode = ionic_ndo_set_rx_mode, 275562306a36Sopenharmony_ci .ndo_set_features = ionic_set_features, 275662306a36Sopenharmony_ci .ndo_set_mac_address = ionic_set_mac_address, 275762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 275862306a36Sopenharmony_ci .ndo_tx_timeout = ionic_tx_timeout, 275962306a36Sopenharmony_ci .ndo_change_mtu = ionic_change_mtu, 276062306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = ionic_vlan_rx_add_vid, 276162306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = ionic_vlan_rx_kill_vid, 276262306a36Sopenharmony_ci .ndo_set_vf_vlan = ionic_set_vf_vlan, 276362306a36Sopenharmony_ci .ndo_set_vf_trust = ionic_set_vf_trust, 276462306a36Sopenharmony_ci .ndo_set_vf_mac = ionic_set_vf_mac, 276562306a36Sopenharmony_ci .ndo_set_vf_rate = ionic_set_vf_rate, 276662306a36Sopenharmony_ci .ndo_set_vf_spoofchk = ionic_set_vf_spoofchk, 276762306a36Sopenharmony_ci .ndo_get_vf_config = ionic_get_vf_config, 276862306a36Sopenharmony_ci .ndo_set_vf_link_state = ionic_set_vf_link_state, 276962306a36Sopenharmony_ci .ndo_get_vf_stats = ionic_get_vf_stats, 277062306a36Sopenharmony_ci}; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_cistatic int ionic_cmb_reconfig(struct ionic_lif *lif, 277362306a36Sopenharmony_ci struct ionic_queue_params *qparam) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci struct ionic_queue_params start_qparams; 277662306a36Sopenharmony_ci int err = 0; 277762306a36Sopenharmony_ci 277862306a36Sopenharmony_ci /* When changing CMB queue parameters, we're using limited 277962306a36Sopenharmony_ci * on-device memory and don't have extra memory to use for 278062306a36Sopenharmony_ci * duplicate allocations, so we free it all first then 278162306a36Sopenharmony_ci * re-allocate with the new parameters. 278262306a36Sopenharmony_ci */ 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci /* Checkpoint for possible unwind */ 278562306a36Sopenharmony_ci ionic_init_queue_params(lif, &start_qparams); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci /* Stop and free the queues */ 278862306a36Sopenharmony_ci ionic_stop_queues_reconfig(lif); 278962306a36Sopenharmony_ci ionic_txrx_free(lif); 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci /* Set up new qparams */ 279262306a36Sopenharmony_ci ionic_set_queue_params(lif, qparam); 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci if (netif_running(lif->netdev)) { 279562306a36Sopenharmony_ci /* Alloc and start the new configuration */ 279662306a36Sopenharmony_ci err = ionic_txrx_alloc(lif); 279762306a36Sopenharmony_ci if (err) { 279862306a36Sopenharmony_ci dev_warn(lif->ionic->dev, 279962306a36Sopenharmony_ci "CMB reconfig failed, restoring values: %d\n", err); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci /* Back out the changes */ 280262306a36Sopenharmony_ci ionic_set_queue_params(lif, &start_qparams); 280362306a36Sopenharmony_ci err = ionic_txrx_alloc(lif); 280462306a36Sopenharmony_ci if (err) { 280562306a36Sopenharmony_ci dev_err(lif->ionic->dev, 280662306a36Sopenharmony_ci "CMB restore failed: %d\n", err); 280762306a36Sopenharmony_ci goto err_out; 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci err = ionic_start_queues_reconfig(lif); 281262306a36Sopenharmony_ci if (err) { 281362306a36Sopenharmony_ci dev_err(lif->ionic->dev, 281462306a36Sopenharmony_ci "CMB reconfig failed: %d\n", err); 281562306a36Sopenharmony_ci goto err_out; 281662306a36Sopenharmony_ci } 281762306a36Sopenharmony_ci } 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_cierr_out: 282062306a36Sopenharmony_ci /* This was detached in ionic_stop_queues_reconfig() */ 282162306a36Sopenharmony_ci netif_device_attach(lif->netdev); 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci return err; 282462306a36Sopenharmony_ci} 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_cistatic void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b) 282762306a36Sopenharmony_ci{ 282862306a36Sopenharmony_ci /* only swapping the queues, not the napi, flags, or other stuff */ 282962306a36Sopenharmony_ci swap(a->q.features, b->q.features); 283062306a36Sopenharmony_ci swap(a->q.num_descs, b->q.num_descs); 283162306a36Sopenharmony_ci swap(a->q.desc_size, b->q.desc_size); 283262306a36Sopenharmony_ci swap(a->q.base, b->q.base); 283362306a36Sopenharmony_ci swap(a->q.base_pa, b->q.base_pa); 283462306a36Sopenharmony_ci swap(a->q.info, b->q.info); 283562306a36Sopenharmony_ci swap(a->q_base, b->q_base); 283662306a36Sopenharmony_ci swap(a->q_base_pa, b->q_base_pa); 283762306a36Sopenharmony_ci swap(a->q_size, b->q_size); 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci swap(a->q.sg_desc_size, b->q.sg_desc_size); 284062306a36Sopenharmony_ci swap(a->q.sg_base, b->q.sg_base); 284162306a36Sopenharmony_ci swap(a->q.sg_base_pa, b->q.sg_base_pa); 284262306a36Sopenharmony_ci swap(a->sg_base, b->sg_base); 284362306a36Sopenharmony_ci swap(a->sg_base_pa, b->sg_base_pa); 284462306a36Sopenharmony_ci swap(a->sg_size, b->sg_size); 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci swap(a->cq.num_descs, b->cq.num_descs); 284762306a36Sopenharmony_ci swap(a->cq.desc_size, b->cq.desc_size); 284862306a36Sopenharmony_ci swap(a->cq.base, b->cq.base); 284962306a36Sopenharmony_ci swap(a->cq.base_pa, b->cq.base_pa); 285062306a36Sopenharmony_ci swap(a->cq.info, b->cq.info); 285162306a36Sopenharmony_ci swap(a->cq_base, b->cq_base); 285262306a36Sopenharmony_ci swap(a->cq_base_pa, b->cq_base_pa); 285362306a36Sopenharmony_ci swap(a->cq_size, b->cq_size); 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci ionic_debugfs_del_qcq(a); 285662306a36Sopenharmony_ci ionic_debugfs_add_qcq(a->q.lif, a); 285762306a36Sopenharmony_ci} 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ciint ionic_reconfigure_queues(struct ionic_lif *lif, 286062306a36Sopenharmony_ci struct ionic_queue_params *qparam) 286162306a36Sopenharmony_ci{ 286262306a36Sopenharmony_ci unsigned int comp_sz, desc_sz, num_desc, sg_desc_sz; 286362306a36Sopenharmony_ci struct ionic_qcq **tx_qcqs = NULL; 286462306a36Sopenharmony_ci struct ionic_qcq **rx_qcqs = NULL; 286562306a36Sopenharmony_ci unsigned int flags, i; 286662306a36Sopenharmony_ci int err = 0; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci /* Are we changing q params while CMB is on */ 286962306a36Sopenharmony_ci if ((test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state) && qparam->cmb_tx) || 287062306a36Sopenharmony_ci (test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state) && qparam->cmb_rx)) 287162306a36Sopenharmony_ci return ionic_cmb_reconfig(lif, qparam); 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci /* allocate temporary qcq arrays to hold new queue structs */ 287462306a36Sopenharmony_ci if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) { 287562306a36Sopenharmony_ci tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->ntxqs_per_lif, 287662306a36Sopenharmony_ci sizeof(struct ionic_qcq *), GFP_KERNEL); 287762306a36Sopenharmony_ci if (!tx_qcqs) { 287862306a36Sopenharmony_ci err = -ENOMEM; 287962306a36Sopenharmony_ci goto err_out; 288062306a36Sopenharmony_ci } 288162306a36Sopenharmony_ci } 288262306a36Sopenharmony_ci if (qparam->nxqs != lif->nxqs || 288362306a36Sopenharmony_ci qparam->nrxq_descs != lif->nrxq_descs || 288462306a36Sopenharmony_ci qparam->rxq_features != lif->rxq_features) { 288562306a36Sopenharmony_ci rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif, 288662306a36Sopenharmony_ci sizeof(struct ionic_qcq *), GFP_KERNEL); 288762306a36Sopenharmony_ci if (!rx_qcqs) { 288862306a36Sopenharmony_ci err = -ENOMEM; 288962306a36Sopenharmony_ci goto err_out; 289062306a36Sopenharmony_ci } 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci /* allocate new desc_info and rings, but leave the interrupt setup 289462306a36Sopenharmony_ci * until later so as to not mess with the still-running queues 289562306a36Sopenharmony_ci */ 289662306a36Sopenharmony_ci if (tx_qcqs) { 289762306a36Sopenharmony_ci num_desc = qparam->ntxq_descs; 289862306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_txq_desc); 289962306a36Sopenharmony_ci comp_sz = sizeof(struct ionic_txq_comp); 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && 290262306a36Sopenharmony_ci lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == 290362306a36Sopenharmony_ci sizeof(struct ionic_txq_sg_desc_v1)) 290462306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); 290562306a36Sopenharmony_ci else 290662306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_txq_sg_desc); 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 290962306a36Sopenharmony_ci /* If missing, short placeholder qcq needed for swap */ 291062306a36Sopenharmony_ci if (!lif->txqcqs[i]) { 291162306a36Sopenharmony_ci flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG; 291262306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, 291362306a36Sopenharmony_ci 4, desc_sz, comp_sz, sg_desc_sz, 291462306a36Sopenharmony_ci lif->kern_pid, &lif->txqcqs[i]); 291562306a36Sopenharmony_ci if (err) 291662306a36Sopenharmony_ci goto err_out; 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci flags = lif->txqcqs[i]->flags & ~IONIC_QCQ_F_INTR; 292062306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, 292162306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 292262306a36Sopenharmony_ci lif->kern_pid, &tx_qcqs[i]); 292362306a36Sopenharmony_ci if (err) 292462306a36Sopenharmony_ci goto err_out; 292562306a36Sopenharmony_ci } 292662306a36Sopenharmony_ci } 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci if (rx_qcqs) { 292962306a36Sopenharmony_ci num_desc = qparam->nrxq_descs; 293062306a36Sopenharmony_ci desc_sz = sizeof(struct ionic_rxq_desc); 293162306a36Sopenharmony_ci comp_sz = sizeof(struct ionic_rxq_comp); 293262306a36Sopenharmony_ci sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci if (qparam->rxq_features & IONIC_Q_F_2X_CQ_DESC) 293562306a36Sopenharmony_ci comp_sz *= 2; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 293862306a36Sopenharmony_ci /* If missing, short placeholder qcq needed for swap */ 293962306a36Sopenharmony_ci if (!lif->rxqcqs[i]) { 294062306a36Sopenharmony_ci flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG; 294162306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, 294262306a36Sopenharmony_ci 4, desc_sz, comp_sz, sg_desc_sz, 294362306a36Sopenharmony_ci lif->kern_pid, &lif->rxqcqs[i]); 294462306a36Sopenharmony_ci if (err) 294562306a36Sopenharmony_ci goto err_out; 294662306a36Sopenharmony_ci } 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_ci flags = lif->rxqcqs[i]->flags & ~IONIC_QCQ_F_INTR; 294962306a36Sopenharmony_ci err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, 295062306a36Sopenharmony_ci num_desc, desc_sz, comp_sz, sg_desc_sz, 295162306a36Sopenharmony_ci lif->kern_pid, &rx_qcqs[i]); 295262306a36Sopenharmony_ci if (err) 295362306a36Sopenharmony_ci goto err_out; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci rx_qcqs[i]->q.features = qparam->rxq_features; 295662306a36Sopenharmony_ci } 295762306a36Sopenharmony_ci } 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci /* stop and clean the queues */ 296062306a36Sopenharmony_ci ionic_stop_queues_reconfig(lif); 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci if (qparam->nxqs != lif->nxqs) { 296362306a36Sopenharmony_ci err = netif_set_real_num_tx_queues(lif->netdev, qparam->nxqs); 296462306a36Sopenharmony_ci if (err) 296562306a36Sopenharmony_ci goto err_out_reinit_unlock; 296662306a36Sopenharmony_ci err = netif_set_real_num_rx_queues(lif->netdev, qparam->nxqs); 296762306a36Sopenharmony_ci if (err) { 296862306a36Sopenharmony_ci netif_set_real_num_tx_queues(lif->netdev, lif->nxqs); 296962306a36Sopenharmony_ci goto err_out_reinit_unlock; 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci /* swap new desc_info and rings, keeping existing interrupt config */ 297462306a36Sopenharmony_ci if (tx_qcqs) { 297562306a36Sopenharmony_ci lif->ntxq_descs = qparam->ntxq_descs; 297662306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) 297762306a36Sopenharmony_ci ionic_swap_queues(lif->txqcqs[i], tx_qcqs[i]); 297862306a36Sopenharmony_ci } 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci if (rx_qcqs) { 298162306a36Sopenharmony_ci lif->nrxq_descs = qparam->nrxq_descs; 298262306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) 298362306a36Sopenharmony_ci ionic_swap_queues(lif->rxqcqs[i], rx_qcqs[i]); 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci /* if we need to change the interrupt layout, this is the time */ 298762306a36Sopenharmony_ci if (qparam->intr_split != test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state) || 298862306a36Sopenharmony_ci qparam->nxqs != lif->nxqs) { 298962306a36Sopenharmony_ci if (qparam->intr_split) { 299062306a36Sopenharmony_ci set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); 299162306a36Sopenharmony_ci } else { 299262306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); 299362306a36Sopenharmony_ci lif->tx_coalesce_usecs = lif->rx_coalesce_usecs; 299462306a36Sopenharmony_ci lif->tx_coalesce_hw = lif->rx_coalesce_hw; 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci /* Clear existing interrupt assignments. We check for NULL here 299862306a36Sopenharmony_ci * because we're checking the whole array for potential qcqs, not 299962306a36Sopenharmony_ci * just those qcqs that have just been set up. 300062306a36Sopenharmony_ci */ 300162306a36Sopenharmony_ci for (i = 0; i < lif->ionic->ntxqs_per_lif; i++) { 300262306a36Sopenharmony_ci if (lif->txqcqs[i]) 300362306a36Sopenharmony_ci ionic_qcq_intr_free(lif, lif->txqcqs[i]); 300462306a36Sopenharmony_ci if (lif->rxqcqs[i]) 300562306a36Sopenharmony_ci ionic_qcq_intr_free(lif, lif->rxqcqs[i]); 300662306a36Sopenharmony_ci } 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci /* re-assign the interrupts */ 300962306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 301062306a36Sopenharmony_ci lif->rxqcqs[i]->flags |= IONIC_QCQ_F_INTR; 301162306a36Sopenharmony_ci err = ionic_alloc_qcq_interrupt(lif, lif->rxqcqs[i]); 301262306a36Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 301362306a36Sopenharmony_ci lif->rxqcqs[i]->intr.index, 301462306a36Sopenharmony_ci lif->rx_coalesce_hw); 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci if (qparam->intr_split) { 301762306a36Sopenharmony_ci lif->txqcqs[i]->flags |= IONIC_QCQ_F_INTR; 301862306a36Sopenharmony_ci err = ionic_alloc_qcq_interrupt(lif, lif->txqcqs[i]); 301962306a36Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 302062306a36Sopenharmony_ci lif->txqcqs[i]->intr.index, 302162306a36Sopenharmony_ci lif->tx_coalesce_hw); 302262306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state)) 302362306a36Sopenharmony_ci lif->txqcqs[i]->intr.dim_coal_hw = lif->tx_coalesce_hw; 302462306a36Sopenharmony_ci } else { 302562306a36Sopenharmony_ci lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; 302662306a36Sopenharmony_ci ionic_link_qcq_interrupts(lif->rxqcqs[i], lif->txqcqs[i]); 302762306a36Sopenharmony_ci } 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci /* now we can rework the debugfs mappings */ 303262306a36Sopenharmony_ci if (tx_qcqs) { 303362306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 303462306a36Sopenharmony_ci ionic_debugfs_del_qcq(lif->txqcqs[i]); 303562306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->txqcqs[i]); 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci } 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci if (rx_qcqs) { 304062306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 304162306a36Sopenharmony_ci ionic_debugfs_del_qcq(lif->rxqcqs[i]); 304262306a36Sopenharmony_ci ionic_debugfs_add_qcq(lif, lif->rxqcqs[i]); 304362306a36Sopenharmony_ci } 304462306a36Sopenharmony_ci } 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci swap(lif->nxqs, qparam->nxqs); 304762306a36Sopenharmony_ci swap(lif->rxq_features, qparam->rxq_features); 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_cierr_out_reinit_unlock: 305062306a36Sopenharmony_ci /* re-init the queues, but don't lose an error code */ 305162306a36Sopenharmony_ci if (err) 305262306a36Sopenharmony_ci ionic_start_queues_reconfig(lif); 305362306a36Sopenharmony_ci else 305462306a36Sopenharmony_ci err = ionic_start_queues_reconfig(lif); 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_cierr_out: 305762306a36Sopenharmony_ci /* free old allocs without cleaning intr */ 305862306a36Sopenharmony_ci for (i = 0; i < qparam->nxqs; i++) { 305962306a36Sopenharmony_ci if (tx_qcqs && tx_qcqs[i]) { 306062306a36Sopenharmony_ci tx_qcqs[i]->flags &= ~IONIC_QCQ_F_INTR; 306162306a36Sopenharmony_ci ionic_qcq_free(lif, tx_qcqs[i]); 306262306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, tx_qcqs[i]); 306362306a36Sopenharmony_ci tx_qcqs[i] = NULL; 306462306a36Sopenharmony_ci } 306562306a36Sopenharmony_ci if (rx_qcqs && rx_qcqs[i]) { 306662306a36Sopenharmony_ci rx_qcqs[i]->flags &= ~IONIC_QCQ_F_INTR; 306762306a36Sopenharmony_ci ionic_qcq_free(lif, rx_qcqs[i]); 306862306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, rx_qcqs[i]); 306962306a36Sopenharmony_ci rx_qcqs[i] = NULL; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci } 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci /* free q array */ 307462306a36Sopenharmony_ci if (rx_qcqs) { 307562306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, rx_qcqs); 307662306a36Sopenharmony_ci rx_qcqs = NULL; 307762306a36Sopenharmony_ci } 307862306a36Sopenharmony_ci if (tx_qcqs) { 307962306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, tx_qcqs); 308062306a36Sopenharmony_ci tx_qcqs = NULL; 308162306a36Sopenharmony_ci } 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci /* clean the unused dma and info allocations when new set is smaller 308462306a36Sopenharmony_ci * than the full array, but leave the qcq shells in place 308562306a36Sopenharmony_ci */ 308662306a36Sopenharmony_ci for (i = lif->nxqs; i < lif->ionic->ntxqs_per_lif; i++) { 308762306a36Sopenharmony_ci if (lif->txqcqs && lif->txqcqs[i]) { 308862306a36Sopenharmony_ci lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; 308962306a36Sopenharmony_ci ionic_qcq_free(lif, lif->txqcqs[i]); 309062306a36Sopenharmony_ci } 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci if (lif->rxqcqs && lif->rxqcqs[i]) { 309362306a36Sopenharmony_ci lif->rxqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; 309462306a36Sopenharmony_ci ionic_qcq_free(lif, lif->rxqcqs[i]); 309562306a36Sopenharmony_ci } 309662306a36Sopenharmony_ci } 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci if (err) 309962306a36Sopenharmony_ci netdev_info(lif->netdev, "%s: failed %d\n", __func__, err); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci return err; 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ciint ionic_lif_alloc(struct ionic *ionic) 310562306a36Sopenharmony_ci{ 310662306a36Sopenharmony_ci struct device *dev = ionic->dev; 310762306a36Sopenharmony_ci union ionic_lif_identity *lid; 310862306a36Sopenharmony_ci struct net_device *netdev; 310962306a36Sopenharmony_ci struct ionic_lif *lif; 311062306a36Sopenharmony_ci int tbl_sz; 311162306a36Sopenharmony_ci int err; 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci lid = kzalloc(sizeof(*lid), GFP_KERNEL); 311462306a36Sopenharmony_ci if (!lid) 311562306a36Sopenharmony_ci return -ENOMEM; 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(*lif), 311862306a36Sopenharmony_ci ionic->ntxqs_per_lif, ionic->ntxqs_per_lif); 311962306a36Sopenharmony_ci if (!netdev) { 312062306a36Sopenharmony_ci dev_err(dev, "Cannot allocate netdev, aborting\n"); 312162306a36Sopenharmony_ci err = -ENOMEM; 312262306a36Sopenharmony_ci goto err_out_free_lid; 312362306a36Sopenharmony_ci } 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, dev); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci lif = netdev_priv(netdev); 312862306a36Sopenharmony_ci lif->netdev = netdev; 312962306a36Sopenharmony_ci ionic->lif = lif; 313062306a36Sopenharmony_ci netdev->netdev_ops = &ionic_netdev_ops; 313162306a36Sopenharmony_ci ionic_ethtool_set_ops(netdev); 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci netdev->watchdog_timeo = 2 * HZ; 313462306a36Sopenharmony_ci netif_carrier_off(netdev); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci lif->identity = lid; 313762306a36Sopenharmony_ci lif->lif_type = IONIC_LIF_TYPE_CLASSIC; 313862306a36Sopenharmony_ci err = ionic_lif_identify(ionic, lif->lif_type, lif->identity); 313962306a36Sopenharmony_ci if (err) { 314062306a36Sopenharmony_ci dev_err(ionic->dev, "Cannot identify type %d: %d\n", 314162306a36Sopenharmony_ci lif->lif_type, err); 314262306a36Sopenharmony_ci goto err_out_free_netdev; 314362306a36Sopenharmony_ci } 314462306a36Sopenharmony_ci lif->netdev->min_mtu = max_t(unsigned int, ETH_MIN_MTU, 314562306a36Sopenharmony_ci le32_to_cpu(lif->identity->eth.min_frame_size)); 314662306a36Sopenharmony_ci lif->netdev->max_mtu = 314762306a36Sopenharmony_ci le32_to_cpu(lif->identity->eth.max_frame_size) - ETH_HLEN - VLAN_HLEN; 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci lif->neqs = ionic->neqs_per_lif; 315062306a36Sopenharmony_ci lif->nxqs = ionic->ntxqs_per_lif; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci lif->ionic = ionic; 315362306a36Sopenharmony_ci lif->index = 0; 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci if (is_kdump_kernel()) { 315662306a36Sopenharmony_ci lif->ntxq_descs = IONIC_MIN_TXRX_DESC; 315762306a36Sopenharmony_ci lif->nrxq_descs = IONIC_MIN_TXRX_DESC; 315862306a36Sopenharmony_ci } else { 315962306a36Sopenharmony_ci lif->ntxq_descs = IONIC_DEF_TXRX_DESC; 316062306a36Sopenharmony_ci lif->nrxq_descs = IONIC_DEF_TXRX_DESC; 316162306a36Sopenharmony_ci } 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci /* Convert the default coalesce value to actual hw resolution */ 316462306a36Sopenharmony_ci lif->rx_coalesce_usecs = IONIC_ITR_COAL_USEC_DEFAULT; 316562306a36Sopenharmony_ci lif->rx_coalesce_hw = ionic_coal_usec_to_hw(lif->ionic, 316662306a36Sopenharmony_ci lif->rx_coalesce_usecs); 316762306a36Sopenharmony_ci lif->tx_coalesce_usecs = lif->rx_coalesce_usecs; 316862306a36Sopenharmony_ci lif->tx_coalesce_hw = lif->rx_coalesce_hw; 316962306a36Sopenharmony_ci set_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state); 317062306a36Sopenharmony_ci set_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci snprintf(lif->name, sizeof(lif->name), "lif%u", lif->index); 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci mutex_init(&lif->queue_lock); 317562306a36Sopenharmony_ci mutex_init(&lif->config_lock); 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci spin_lock_init(&lif->adminq_lock); 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci spin_lock_init(&lif->deferred.lock); 318062306a36Sopenharmony_ci INIT_LIST_HEAD(&lif->deferred.list); 318162306a36Sopenharmony_ci INIT_WORK(&lif->deferred.work, ionic_lif_deferred_work); 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci /* allocate lif info */ 318462306a36Sopenharmony_ci lif->info_sz = ALIGN(sizeof(*lif->info), PAGE_SIZE); 318562306a36Sopenharmony_ci lif->info = dma_alloc_coherent(dev, lif->info_sz, 318662306a36Sopenharmony_ci &lif->info_pa, GFP_KERNEL); 318762306a36Sopenharmony_ci if (!lif->info) { 318862306a36Sopenharmony_ci dev_err(dev, "Failed to allocate lif info, aborting\n"); 318962306a36Sopenharmony_ci err = -ENOMEM; 319062306a36Sopenharmony_ci goto err_out_free_mutex; 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci ionic_debugfs_add_lif(lif); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci /* allocate control queues and txrx queue arrays */ 319662306a36Sopenharmony_ci ionic_lif_queue_identify(lif); 319762306a36Sopenharmony_ci err = ionic_qcqs_alloc(lif); 319862306a36Sopenharmony_ci if (err) 319962306a36Sopenharmony_ci goto err_out_free_lif_info; 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci /* allocate rss indirection table */ 320262306a36Sopenharmony_ci tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 320362306a36Sopenharmony_ci lif->rss_ind_tbl_sz = sizeof(*lif->rss_ind_tbl) * tbl_sz; 320462306a36Sopenharmony_ci lif->rss_ind_tbl = dma_alloc_coherent(dev, lif->rss_ind_tbl_sz, 320562306a36Sopenharmony_ci &lif->rss_ind_tbl_pa, 320662306a36Sopenharmony_ci GFP_KERNEL); 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci if (!lif->rss_ind_tbl) { 320962306a36Sopenharmony_ci err = -ENOMEM; 321062306a36Sopenharmony_ci dev_err(dev, "Failed to allocate rss indirection table, aborting\n"); 321162306a36Sopenharmony_ci goto err_out_free_qcqs; 321262306a36Sopenharmony_ci } 321362306a36Sopenharmony_ci netdev_rss_key_fill(lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE); 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci ionic_lif_alloc_phc(lif); 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci return 0; 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_cierr_out_free_qcqs: 322062306a36Sopenharmony_ci ionic_qcqs_free(lif); 322162306a36Sopenharmony_cierr_out_free_lif_info: 322262306a36Sopenharmony_ci dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa); 322362306a36Sopenharmony_ci lif->info = NULL; 322462306a36Sopenharmony_ci lif->info_pa = 0; 322562306a36Sopenharmony_cierr_out_free_mutex: 322662306a36Sopenharmony_ci mutex_destroy(&lif->config_lock); 322762306a36Sopenharmony_ci mutex_destroy(&lif->queue_lock); 322862306a36Sopenharmony_cierr_out_free_netdev: 322962306a36Sopenharmony_ci free_netdev(lif->netdev); 323062306a36Sopenharmony_ci lif = NULL; 323162306a36Sopenharmony_cierr_out_free_lid: 323262306a36Sopenharmony_ci kfree(lid); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci return err; 323562306a36Sopenharmony_ci} 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_cistatic void ionic_lif_reset(struct ionic_lif *lif) 323862306a36Sopenharmony_ci{ 323962306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci if (!ionic_is_fw_running(idev)) 324262306a36Sopenharmony_ci return; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci mutex_lock(&lif->ionic->dev_cmd_lock); 324562306a36Sopenharmony_ci ionic_dev_cmd_lif_reset(idev, lif->index); 324662306a36Sopenharmony_ci ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); 324762306a36Sopenharmony_ci mutex_unlock(&lif->ionic->dev_cmd_lock); 324862306a36Sopenharmony_ci} 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_cistatic void ionic_lif_handle_fw_down(struct ionic_lif *lif) 325162306a36Sopenharmony_ci{ 325262306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci if (test_and_set_bit(IONIC_LIF_F_FW_RESET, lif->state)) 325562306a36Sopenharmony_ci return; 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci dev_info(ionic->dev, "FW Down: Stopping LIFs\n"); 325862306a36Sopenharmony_ci 325962306a36Sopenharmony_ci netif_device_detach(lif->netdev); 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 326262306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_UP, lif->state)) { 326362306a36Sopenharmony_ci dev_info(ionic->dev, "Surprise FW stop, stopping queues\n"); 326462306a36Sopenharmony_ci ionic_stop_queues(lif); 326562306a36Sopenharmony_ci } 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci if (netif_running(lif->netdev)) { 326862306a36Sopenharmony_ci ionic_txrx_deinit(lif); 326962306a36Sopenharmony_ci ionic_txrx_free(lif); 327062306a36Sopenharmony_ci } 327162306a36Sopenharmony_ci ionic_lif_deinit(lif); 327262306a36Sopenharmony_ci ionic_reset(ionic); 327362306a36Sopenharmony_ci ionic_qcqs_free(lif); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); 327862306a36Sopenharmony_ci dev_info(ionic->dev, "FW Down: LIFs stopped\n"); 327962306a36Sopenharmony_ci} 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ciint ionic_restart_lif(struct ionic_lif *lif) 328262306a36Sopenharmony_ci{ 328362306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 328462306a36Sopenharmony_ci int err; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state)) 328962306a36Sopenharmony_ci dev_info(ionic->dev, "FW Up: clearing broken state\n"); 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci err = ionic_qcqs_alloc(lif); 329262306a36Sopenharmony_ci if (err) 329362306a36Sopenharmony_ci goto err_unlock; 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci err = ionic_lif_init(lif); 329662306a36Sopenharmony_ci if (err) 329762306a36Sopenharmony_ci goto err_qcqs_free; 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci ionic_vf_attr_replay(lif); 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci if (lif->registered) 330262306a36Sopenharmony_ci ionic_lif_set_netdev_info(lif); 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci ionic_rx_filter_replay(lif); 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci if (netif_running(lif->netdev)) { 330762306a36Sopenharmony_ci err = ionic_txrx_alloc(lif); 330862306a36Sopenharmony_ci if (err) 330962306a36Sopenharmony_ci goto err_lifs_deinit; 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci err = ionic_txrx_init(lif); 331262306a36Sopenharmony_ci if (err) 331362306a36Sopenharmony_ci goto err_txrx_free; 331462306a36Sopenharmony_ci } 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci clear_bit(IONIC_LIF_F_FW_RESET, lif->state); 331962306a36Sopenharmony_ci ionic_link_status_check_request(lif, CAN_SLEEP); 332062306a36Sopenharmony_ci netif_device_attach(lif->netdev); 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci return 0; 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_cierr_txrx_free: 332562306a36Sopenharmony_ci ionic_txrx_free(lif); 332662306a36Sopenharmony_cierr_lifs_deinit: 332762306a36Sopenharmony_ci ionic_lif_deinit(lif); 332862306a36Sopenharmony_cierr_qcqs_free: 332962306a36Sopenharmony_ci ionic_qcqs_free(lif); 333062306a36Sopenharmony_cierr_unlock: 333162306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci return err; 333462306a36Sopenharmony_ci} 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_cistatic void ionic_lif_handle_fw_up(struct ionic_lif *lif) 333762306a36Sopenharmony_ci{ 333862306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 333962306a36Sopenharmony_ci int err; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) 334262306a36Sopenharmony_ci return; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci dev_info(ionic->dev, "FW Up: restarting LIFs\n"); 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci /* This is a little different from what happens at 334762306a36Sopenharmony_ci * probe time because the LIF already exists so we 334862306a36Sopenharmony_ci * just need to reanimate it. 334962306a36Sopenharmony_ci */ 335062306a36Sopenharmony_ci ionic_init_devinfo(ionic); 335162306a36Sopenharmony_ci err = ionic_identify(ionic); 335262306a36Sopenharmony_ci if (err) 335362306a36Sopenharmony_ci goto err_out; 335462306a36Sopenharmony_ci err = ionic_port_identify(ionic); 335562306a36Sopenharmony_ci if (err) 335662306a36Sopenharmony_ci goto err_out; 335762306a36Sopenharmony_ci err = ionic_port_init(ionic); 335862306a36Sopenharmony_ci if (err) 335962306a36Sopenharmony_ci goto err_out; 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci err = ionic_restart_lif(lif); 336262306a36Sopenharmony_ci if (err) 336362306a36Sopenharmony_ci goto err_out; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci dev_info(ionic->dev, "FW Up: LIFs restarted\n"); 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci /* restore the hardware timestamping queues */ 336862306a36Sopenharmony_ci ionic_lif_hwstamp_replay(lif); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci return; 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_cierr_out: 337362306a36Sopenharmony_ci dev_err(ionic->dev, "FW Up: LIFs restart failed - err %d\n", err); 337462306a36Sopenharmony_ci} 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_civoid ionic_lif_free(struct ionic_lif *lif) 337762306a36Sopenharmony_ci{ 337862306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci ionic_lif_free_phc(lif); 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci /* free rss indirection table */ 338362306a36Sopenharmony_ci dma_free_coherent(dev, lif->rss_ind_tbl_sz, lif->rss_ind_tbl, 338462306a36Sopenharmony_ci lif->rss_ind_tbl_pa); 338562306a36Sopenharmony_ci lif->rss_ind_tbl = NULL; 338662306a36Sopenharmony_ci lif->rss_ind_tbl_pa = 0; 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci /* free queues */ 338962306a36Sopenharmony_ci ionic_qcqs_free(lif); 339062306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) 339162306a36Sopenharmony_ci ionic_lif_reset(lif); 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci /* free lif info */ 339462306a36Sopenharmony_ci kfree(lif->identity); 339562306a36Sopenharmony_ci dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa); 339662306a36Sopenharmony_ci lif->info = NULL; 339762306a36Sopenharmony_ci lif->info_pa = 0; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci /* unmap doorbell page */ 340062306a36Sopenharmony_ci ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); 340162306a36Sopenharmony_ci lif->kern_dbpage = NULL; 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci mutex_destroy(&lif->config_lock); 340462306a36Sopenharmony_ci mutex_destroy(&lif->queue_lock); 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci /* free netdev & lif */ 340762306a36Sopenharmony_ci ionic_debugfs_del_lif(lif); 340862306a36Sopenharmony_ci free_netdev(lif->netdev); 340962306a36Sopenharmony_ci} 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_civoid ionic_lif_deinit(struct ionic_lif *lif) 341262306a36Sopenharmony_ci{ 341362306a36Sopenharmony_ci if (!test_and_clear_bit(IONIC_LIF_F_INITED, lif->state)) 341462306a36Sopenharmony_ci return; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { 341762306a36Sopenharmony_ci cancel_work_sync(&lif->deferred.work); 341862306a36Sopenharmony_ci cancel_work_sync(&lif->tx_timeout_work); 341962306a36Sopenharmony_ci ionic_rx_filters_deinit(lif); 342062306a36Sopenharmony_ci if (lif->netdev->features & NETIF_F_RXHASH) 342162306a36Sopenharmony_ci ionic_lif_rss_deinit(lif); 342262306a36Sopenharmony_ci } 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci napi_disable(&lif->adminqcq->napi); 342562306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->notifyqcq); 342662306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->adminqcq); 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_ci ionic_lif_reset(lif); 342962306a36Sopenharmony_ci} 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_cistatic int ionic_lif_adminq_init(struct ionic_lif *lif) 343262306a36Sopenharmony_ci{ 343362306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 343462306a36Sopenharmony_ci struct ionic_q_init_comp comp; 343562306a36Sopenharmony_ci struct ionic_dev *idev; 343662306a36Sopenharmony_ci struct ionic_qcq *qcq; 343762306a36Sopenharmony_ci struct ionic_queue *q; 343862306a36Sopenharmony_ci int err; 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci idev = &lif->ionic->idev; 344162306a36Sopenharmony_ci qcq = lif->adminqcq; 344262306a36Sopenharmony_ci q = &qcq->q; 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci mutex_lock(&lif->ionic->dev_cmd_lock); 344562306a36Sopenharmony_ci ionic_dev_cmd_adminq_init(idev, qcq, lif->index, qcq->intr.index); 344662306a36Sopenharmony_ci err = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); 344762306a36Sopenharmony_ci ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp); 344862306a36Sopenharmony_ci mutex_unlock(&lif->ionic->dev_cmd_lock); 344962306a36Sopenharmony_ci if (err) { 345062306a36Sopenharmony_ci netdev_err(lif->netdev, "adminq init failed %d\n", err); 345162306a36Sopenharmony_ci return err; 345262306a36Sopenharmony_ci } 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci q->hw_type = comp.hw_type; 345562306a36Sopenharmony_ci q->hw_index = le32_to_cpu(comp.hw_index); 345662306a36Sopenharmony_ci q->dbval = IONIC_DBELL_QID(q->hw_index); 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci dev_dbg(dev, "adminq->hw_type %d\n", q->hw_type); 345962306a36Sopenharmony_ci dev_dbg(dev, "adminq->hw_index %d\n", q->hw_index); 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci q->dbell_deadline = IONIC_ADMIN_DOORBELL_DEADLINE; 346262306a36Sopenharmony_ci q->dbell_jiffies = jiffies; 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_ci netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi); 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci qcq->napi_qcq = qcq; 346762306a36Sopenharmony_ci timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci napi_enable(&qcq->napi); 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci if (qcq->flags & IONIC_QCQ_F_INTR) 347262306a36Sopenharmony_ci ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, 347362306a36Sopenharmony_ci IONIC_INTR_MASK_CLEAR); 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci qcq->flags |= IONIC_QCQ_F_INITED; 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci return 0; 347862306a36Sopenharmony_ci} 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_cistatic int ionic_lif_notifyq_init(struct ionic_lif *lif) 348162306a36Sopenharmony_ci{ 348262306a36Sopenharmony_ci struct ionic_qcq *qcq = lif->notifyqcq; 348362306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 348462306a36Sopenharmony_ci struct ionic_queue *q = &qcq->q; 348562306a36Sopenharmony_ci int err; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 348862306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 348962306a36Sopenharmony_ci .cmd.q_init = { 349062306a36Sopenharmony_ci .opcode = IONIC_CMD_Q_INIT, 349162306a36Sopenharmony_ci .lif_index = cpu_to_le16(lif->index), 349262306a36Sopenharmony_ci .type = q->type, 349362306a36Sopenharmony_ci .ver = lif->qtype_info[q->type].version, 349462306a36Sopenharmony_ci .index = cpu_to_le32(q->index), 349562306a36Sopenharmony_ci .flags = cpu_to_le16(IONIC_QINIT_F_IRQ | 349662306a36Sopenharmony_ci IONIC_QINIT_F_ENA), 349762306a36Sopenharmony_ci .intr_index = cpu_to_le16(lif->adminqcq->intr.index), 349862306a36Sopenharmony_ci .pid = cpu_to_le16(q->pid), 349962306a36Sopenharmony_ci .ring_size = ilog2(q->num_descs), 350062306a36Sopenharmony_ci .ring_base = cpu_to_le64(q->base_pa), 350162306a36Sopenharmony_ci } 350262306a36Sopenharmony_ci }; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci dev_dbg(dev, "notifyq_init.pid %d\n", ctx.cmd.q_init.pid); 350562306a36Sopenharmony_ci dev_dbg(dev, "notifyq_init.index %d\n", ctx.cmd.q_init.index); 350662306a36Sopenharmony_ci dev_dbg(dev, "notifyq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base); 350762306a36Sopenharmony_ci dev_dbg(dev, "notifyq_init.ring_size %d\n", ctx.cmd.q_init.ring_size); 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 351062306a36Sopenharmony_ci if (err) 351162306a36Sopenharmony_ci return err; 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci lif->last_eid = 0; 351462306a36Sopenharmony_ci q->hw_type = ctx.comp.q_init.hw_type; 351562306a36Sopenharmony_ci q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index); 351662306a36Sopenharmony_ci q->dbval = IONIC_DBELL_QID(q->hw_index); 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci dev_dbg(dev, "notifyq->hw_type %d\n", q->hw_type); 351962306a36Sopenharmony_ci dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index); 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci /* preset the callback info */ 352262306a36Sopenharmony_ci q->info[0].cb_arg = lif; 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci qcq->flags |= IONIC_QCQ_F_INITED; 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci return 0; 352762306a36Sopenharmony_ci} 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_cistatic int ionic_station_set(struct ionic_lif *lif) 353062306a36Sopenharmony_ci{ 353162306a36Sopenharmony_ci struct net_device *netdev = lif->netdev; 353262306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 353362306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 353462306a36Sopenharmony_ci .cmd.lif_getattr = { 353562306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_GETATTR, 353662306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 353762306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_MAC, 353862306a36Sopenharmony_ci }, 353962306a36Sopenharmony_ci }; 354062306a36Sopenharmony_ci u8 mac_address[ETH_ALEN]; 354162306a36Sopenharmony_ci struct sockaddr addr; 354262306a36Sopenharmony_ci int err; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci err = ionic_adminq_post_wait(lif, &ctx); 354562306a36Sopenharmony_ci if (err) 354662306a36Sopenharmony_ci return err; 354762306a36Sopenharmony_ci netdev_dbg(lif->netdev, "found initial MAC addr %pM\n", 354862306a36Sopenharmony_ci ctx.comp.lif_getattr.mac); 354962306a36Sopenharmony_ci ether_addr_copy(mac_address, ctx.comp.lif_getattr.mac); 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci if (is_zero_ether_addr(mac_address)) { 355262306a36Sopenharmony_ci eth_hw_addr_random(netdev); 355362306a36Sopenharmony_ci netdev_dbg(netdev, "Random Mac generated: %pM\n", netdev->dev_addr); 355462306a36Sopenharmony_ci ether_addr_copy(mac_address, netdev->dev_addr); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci err = ionic_program_mac(lif, mac_address); 355762306a36Sopenharmony_ci if (err < 0) 355862306a36Sopenharmony_ci return err; 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci if (err > 0) { 356162306a36Sopenharmony_ci netdev_dbg(netdev, "%s:SET/GET ATTR Mac are not same-due to old FW running\n", 356262306a36Sopenharmony_ci __func__); 356362306a36Sopenharmony_ci return 0; 356462306a36Sopenharmony_ci } 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci if (!is_zero_ether_addr(netdev->dev_addr)) { 356862306a36Sopenharmony_ci /* If the netdev mac is non-zero and doesn't match the default 356962306a36Sopenharmony_ci * device address, it was set by something earlier and we're 357062306a36Sopenharmony_ci * likely here again after a fw-upgrade reset. We need to be 357162306a36Sopenharmony_ci * sure the netdev mac is in our filter list. 357262306a36Sopenharmony_ci */ 357362306a36Sopenharmony_ci if (!ether_addr_equal(mac_address, netdev->dev_addr)) 357462306a36Sopenharmony_ci ionic_lif_addr_add(lif, netdev->dev_addr); 357562306a36Sopenharmony_ci } else { 357662306a36Sopenharmony_ci /* Update the netdev mac with the device's mac */ 357762306a36Sopenharmony_ci ether_addr_copy(addr.sa_data, mac_address); 357862306a36Sopenharmony_ci addr.sa_family = AF_INET; 357962306a36Sopenharmony_ci err = eth_prepare_mac_addr_change(netdev, &addr); 358062306a36Sopenharmony_ci if (err) { 358162306a36Sopenharmony_ci netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n", 358262306a36Sopenharmony_ci addr.sa_data, err); 358362306a36Sopenharmony_ci return 0; 358462306a36Sopenharmony_ci } 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci eth_commit_mac_addr_change(netdev, &addr); 358762306a36Sopenharmony_ci } 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci netdev_dbg(lif->netdev, "adding station MAC addr %pM\n", 359062306a36Sopenharmony_ci netdev->dev_addr); 359162306a36Sopenharmony_ci ionic_lif_addr_add(lif, netdev->dev_addr); 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci return 0; 359462306a36Sopenharmony_ci} 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ciint ionic_lif_init(struct ionic_lif *lif) 359762306a36Sopenharmony_ci{ 359862306a36Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 359962306a36Sopenharmony_ci struct device *dev = lif->ionic->dev; 360062306a36Sopenharmony_ci struct ionic_lif_init_comp comp; 360162306a36Sopenharmony_ci int dbpage_num; 360262306a36Sopenharmony_ci int err; 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci mutex_lock(&lif->ionic->dev_cmd_lock); 360562306a36Sopenharmony_ci ionic_dev_cmd_lif_init(idev, lif->index, lif->info_pa); 360662306a36Sopenharmony_ci err = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); 360762306a36Sopenharmony_ci ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp); 360862306a36Sopenharmony_ci mutex_unlock(&lif->ionic->dev_cmd_lock); 360962306a36Sopenharmony_ci if (err) 361062306a36Sopenharmony_ci return err; 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci lif->hw_index = le16_to_cpu(comp.hw_index); 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_ci /* now that we have the hw_index we can figure out our doorbell page */ 361562306a36Sopenharmony_ci lif->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif); 361662306a36Sopenharmony_ci if (!lif->dbid_count) { 361762306a36Sopenharmony_ci dev_err(dev, "No doorbell pages, aborting\n"); 361862306a36Sopenharmony_ci return -EINVAL; 361962306a36Sopenharmony_ci } 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci lif->kern_pid = 0; 362262306a36Sopenharmony_ci dbpage_num = ionic_db_page_num(lif, lif->kern_pid); 362362306a36Sopenharmony_ci lif->kern_dbpage = ionic_bus_map_dbpage(lif->ionic, dbpage_num); 362462306a36Sopenharmony_ci if (!lif->kern_dbpage) { 362562306a36Sopenharmony_ci dev_err(dev, "Cannot map dbpage, aborting\n"); 362662306a36Sopenharmony_ci return -ENOMEM; 362762306a36Sopenharmony_ci } 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci err = ionic_lif_adminq_init(lif); 363062306a36Sopenharmony_ci if (err) 363162306a36Sopenharmony_ci goto err_out_adminq_deinit; 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci if (lif->ionic->nnqs_per_lif) { 363462306a36Sopenharmony_ci err = ionic_lif_notifyq_init(lif); 363562306a36Sopenharmony_ci if (err) 363662306a36Sopenharmony_ci goto err_out_notifyq_deinit; 363762306a36Sopenharmony_ci } 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci err = ionic_init_nic_features(lif); 364062306a36Sopenharmony_ci if (err) 364162306a36Sopenharmony_ci goto err_out_notifyq_deinit; 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { 364462306a36Sopenharmony_ci err = ionic_rx_filters_init(lif); 364562306a36Sopenharmony_ci if (err) 364662306a36Sopenharmony_ci goto err_out_notifyq_deinit; 364762306a36Sopenharmony_ci } 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_ci err = ionic_station_set(lif); 365062306a36Sopenharmony_ci if (err) 365162306a36Sopenharmony_ci goto err_out_notifyq_deinit; 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci lif->rx_copybreak = IONIC_RX_COPYBREAK_DEFAULT; 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_ci set_bit(IONIC_LIF_F_INITED, lif->state); 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_ci INIT_WORK(&lif->tx_timeout_work, ionic_tx_timeout_work); 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci return 0; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_cierr_out_notifyq_deinit: 366262306a36Sopenharmony_ci napi_disable(&lif->adminqcq->napi); 366362306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->notifyqcq); 366462306a36Sopenharmony_cierr_out_adminq_deinit: 366562306a36Sopenharmony_ci ionic_lif_qcq_deinit(lif, lif->adminqcq); 366662306a36Sopenharmony_ci ionic_lif_reset(lif); 366762306a36Sopenharmony_ci ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); 366862306a36Sopenharmony_ci lif->kern_dbpage = NULL; 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci return err; 367162306a36Sopenharmony_ci} 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_cistatic void ionic_lif_notify_work(struct work_struct *ws) 367462306a36Sopenharmony_ci{ 367562306a36Sopenharmony_ci} 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_cistatic void ionic_lif_set_netdev_info(struct ionic_lif *lif) 367862306a36Sopenharmony_ci{ 367962306a36Sopenharmony_ci struct ionic_admin_ctx ctx = { 368062306a36Sopenharmony_ci .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 368162306a36Sopenharmony_ci .cmd.lif_setattr = { 368262306a36Sopenharmony_ci .opcode = IONIC_CMD_LIF_SETATTR, 368362306a36Sopenharmony_ci .index = cpu_to_le16(lif->index), 368462306a36Sopenharmony_ci .attr = IONIC_LIF_ATTR_NAME, 368562306a36Sopenharmony_ci }, 368662306a36Sopenharmony_ci }; 368762306a36Sopenharmony_ci 368862306a36Sopenharmony_ci strscpy(ctx.cmd.lif_setattr.name, lif->netdev->name, 368962306a36Sopenharmony_ci sizeof(ctx.cmd.lif_setattr.name)); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci ionic_adminq_post_wait(lif, &ctx); 369262306a36Sopenharmony_ci} 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_cistatic struct ionic_lif *ionic_netdev_lif(struct net_device *netdev) 369562306a36Sopenharmony_ci{ 369662306a36Sopenharmony_ci if (!netdev || netdev->netdev_ops->ndo_start_xmit != ionic_start_xmit) 369762306a36Sopenharmony_ci return NULL; 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci return netdev_priv(netdev); 370062306a36Sopenharmony_ci} 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_cistatic int ionic_lif_notify(struct notifier_block *nb, 370362306a36Sopenharmony_ci unsigned long event, void *info) 370462306a36Sopenharmony_ci{ 370562306a36Sopenharmony_ci struct net_device *ndev = netdev_notifier_info_to_dev(info); 370662306a36Sopenharmony_ci struct ionic *ionic = container_of(nb, struct ionic, nb); 370762306a36Sopenharmony_ci struct ionic_lif *lif = ionic_netdev_lif(ndev); 370862306a36Sopenharmony_ci 370962306a36Sopenharmony_ci if (!lif || lif->ionic != ionic) 371062306a36Sopenharmony_ci return NOTIFY_DONE; 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci switch (event) { 371362306a36Sopenharmony_ci case NETDEV_CHANGENAME: 371462306a36Sopenharmony_ci ionic_lif_set_netdev_info(lif); 371562306a36Sopenharmony_ci break; 371662306a36Sopenharmony_ci } 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci return NOTIFY_DONE; 371962306a36Sopenharmony_ci} 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ciint ionic_lif_register(struct ionic_lif *lif) 372262306a36Sopenharmony_ci{ 372362306a36Sopenharmony_ci int err; 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci ionic_lif_register_phc(lif); 372662306a36Sopenharmony_ci 372762306a36Sopenharmony_ci INIT_WORK(&lif->ionic->nb_work, ionic_lif_notify_work); 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci lif->ionic->nb.notifier_call = ionic_lif_notify; 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_ci err = register_netdevice_notifier(&lif->ionic->nb); 373262306a36Sopenharmony_ci if (err) 373362306a36Sopenharmony_ci lif->ionic->nb.notifier_call = NULL; 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci /* only register LIF0 for now */ 373662306a36Sopenharmony_ci err = register_netdev(lif->netdev); 373762306a36Sopenharmony_ci if (err) { 373862306a36Sopenharmony_ci dev_err(lif->ionic->dev, "Cannot register net device, aborting\n"); 373962306a36Sopenharmony_ci ionic_lif_unregister_phc(lif); 374062306a36Sopenharmony_ci return err; 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci ionic_link_status_check_request(lif, CAN_SLEEP); 374462306a36Sopenharmony_ci lif->registered = true; 374562306a36Sopenharmony_ci ionic_lif_set_netdev_info(lif); 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci return 0; 374862306a36Sopenharmony_ci} 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_civoid ionic_lif_unregister(struct ionic_lif *lif) 375162306a36Sopenharmony_ci{ 375262306a36Sopenharmony_ci if (lif->ionic->nb.notifier_call) { 375362306a36Sopenharmony_ci unregister_netdevice_notifier(&lif->ionic->nb); 375462306a36Sopenharmony_ci cancel_work_sync(&lif->ionic->nb_work); 375562306a36Sopenharmony_ci lif->ionic->nb.notifier_call = NULL; 375662306a36Sopenharmony_ci } 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_ci if (lif->netdev->reg_state == NETREG_REGISTERED) 375962306a36Sopenharmony_ci unregister_netdev(lif->netdev); 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci ionic_lif_unregister_phc(lif); 376262306a36Sopenharmony_ci 376362306a36Sopenharmony_ci lif->registered = false; 376462306a36Sopenharmony_ci} 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_cistatic void ionic_lif_queue_identify(struct ionic_lif *lif) 376762306a36Sopenharmony_ci{ 376862306a36Sopenharmony_ci union ionic_q_identity __iomem *q_ident; 376962306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 377062306a36Sopenharmony_ci struct ionic_dev *idev; 377162306a36Sopenharmony_ci int qtype; 377262306a36Sopenharmony_ci int err; 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ci idev = &lif->ionic->idev; 377562306a36Sopenharmony_ci q_ident = (union ionic_q_identity __iomem *)&idev->dev_cmd_regs->data; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci for (qtype = 0; qtype < ARRAY_SIZE(ionic_qtype_versions); qtype++) { 377862306a36Sopenharmony_ci struct ionic_qtype_info *qti = &lif->qtype_info[qtype]; 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci /* filter out the ones we know about */ 378162306a36Sopenharmony_ci switch (qtype) { 378262306a36Sopenharmony_ci case IONIC_QTYPE_ADMINQ: 378362306a36Sopenharmony_ci case IONIC_QTYPE_NOTIFYQ: 378462306a36Sopenharmony_ci case IONIC_QTYPE_RXQ: 378562306a36Sopenharmony_ci case IONIC_QTYPE_TXQ: 378662306a36Sopenharmony_ci break; 378762306a36Sopenharmony_ci default: 378862306a36Sopenharmony_ci continue; 378962306a36Sopenharmony_ci } 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ci memset(qti, 0, sizeof(*qti)); 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 379462306a36Sopenharmony_ci ionic_dev_cmd_queue_identify(idev, lif->lif_type, qtype, 379562306a36Sopenharmony_ci ionic_qtype_versions[qtype]); 379662306a36Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 379762306a36Sopenharmony_ci if (!err) { 379862306a36Sopenharmony_ci qti->version = readb(&q_ident->version); 379962306a36Sopenharmony_ci qti->supported = readb(&q_ident->supported); 380062306a36Sopenharmony_ci qti->features = readq(&q_ident->features); 380162306a36Sopenharmony_ci qti->desc_sz = readw(&q_ident->desc_sz); 380262306a36Sopenharmony_ci qti->comp_sz = readw(&q_ident->comp_sz); 380362306a36Sopenharmony_ci qti->sg_desc_sz = readw(&q_ident->sg_desc_sz); 380462306a36Sopenharmony_ci qti->max_sg_elems = readw(&q_ident->max_sg_elems); 380562306a36Sopenharmony_ci qti->sg_desc_stride = readw(&q_ident->sg_desc_stride); 380662306a36Sopenharmony_ci } 380762306a36Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci if (err == -EINVAL) { 381062306a36Sopenharmony_ci dev_err(ionic->dev, "qtype %d not supported\n", qtype); 381162306a36Sopenharmony_ci continue; 381262306a36Sopenharmony_ci } else if (err == -EIO) { 381362306a36Sopenharmony_ci dev_err(ionic->dev, "q_ident failed, not supported on older FW\n"); 381462306a36Sopenharmony_ci return; 381562306a36Sopenharmony_ci } else if (err) { 381662306a36Sopenharmony_ci dev_err(ionic->dev, "q_ident failed, qtype %d: %d\n", 381762306a36Sopenharmony_ci qtype, err); 381862306a36Sopenharmony_ci return; 381962306a36Sopenharmony_ci } 382062306a36Sopenharmony_ci 382162306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].version = %d\n", 382262306a36Sopenharmony_ci qtype, qti->version); 382362306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].supported = 0x%02x\n", 382462306a36Sopenharmony_ci qtype, qti->supported); 382562306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].features = 0x%04llx\n", 382662306a36Sopenharmony_ci qtype, qti->features); 382762306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].desc_sz = %d\n", 382862306a36Sopenharmony_ci qtype, qti->desc_sz); 382962306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].comp_sz = %d\n", 383062306a36Sopenharmony_ci qtype, qti->comp_sz); 383162306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].sg_desc_sz = %d\n", 383262306a36Sopenharmony_ci qtype, qti->sg_desc_sz); 383362306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].max_sg_elems = %d\n", 383462306a36Sopenharmony_ci qtype, qti->max_sg_elems); 383562306a36Sopenharmony_ci dev_dbg(ionic->dev, " qtype[%d].sg_desc_stride = %d\n", 383662306a36Sopenharmony_ci qtype, qti->sg_desc_stride); 383762306a36Sopenharmony_ci } 383862306a36Sopenharmony_ci} 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ciint ionic_lif_identify(struct ionic *ionic, u8 lif_type, 384162306a36Sopenharmony_ci union ionic_lif_identity *lid) 384262306a36Sopenharmony_ci{ 384362306a36Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 384462306a36Sopenharmony_ci size_t sz; 384562306a36Sopenharmony_ci int err; 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci sz = min(sizeof(*lid), sizeof(idev->dev_cmd_regs->data)); 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 385062306a36Sopenharmony_ci ionic_dev_cmd_lif_identify(idev, lif_type, IONIC_IDENTITY_VERSION_1); 385162306a36Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 385262306a36Sopenharmony_ci memcpy_fromio(lid, &idev->dev_cmd_regs->data, sz); 385362306a36Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 385462306a36Sopenharmony_ci if (err) 385562306a36Sopenharmony_ci return (err); 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci dev_dbg(ionic->dev, "capabilities 0x%llx\n", 385862306a36Sopenharmony_ci le64_to_cpu(lid->capabilities)); 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.max_ucast_filters %d\n", 386162306a36Sopenharmony_ci le32_to_cpu(lid->eth.max_ucast_filters)); 386262306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.max_mcast_filters %d\n", 386362306a36Sopenharmony_ci le32_to_cpu(lid->eth.max_mcast_filters)); 386462306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.features 0x%llx\n", 386562306a36Sopenharmony_ci le64_to_cpu(lid->eth.config.features)); 386662306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.queue_count[IONIC_QTYPE_ADMINQ] %d\n", 386762306a36Sopenharmony_ci le32_to_cpu(lid->eth.config.queue_count[IONIC_QTYPE_ADMINQ])); 386862306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.queue_count[IONIC_QTYPE_NOTIFYQ] %d\n", 386962306a36Sopenharmony_ci le32_to_cpu(lid->eth.config.queue_count[IONIC_QTYPE_NOTIFYQ])); 387062306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.queue_count[IONIC_QTYPE_RXQ] %d\n", 387162306a36Sopenharmony_ci le32_to_cpu(lid->eth.config.queue_count[IONIC_QTYPE_RXQ])); 387262306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.queue_count[IONIC_QTYPE_TXQ] %d\n", 387362306a36Sopenharmony_ci le32_to_cpu(lid->eth.config.queue_count[IONIC_QTYPE_TXQ])); 387462306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.config.name %s\n", lid->eth.config.name); 387562306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.config.mac %pM\n", lid->eth.config.mac); 387662306a36Sopenharmony_ci dev_dbg(ionic->dev, "eth.config.mtu %d\n", 387762306a36Sopenharmony_ci le32_to_cpu(lid->eth.config.mtu)); 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci return 0; 388062306a36Sopenharmony_ci} 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_ciint ionic_lif_size(struct ionic *ionic) 388362306a36Sopenharmony_ci{ 388462306a36Sopenharmony_ci struct ionic_identity *ident = &ionic->ident; 388562306a36Sopenharmony_ci unsigned int nintrs, dev_nintrs; 388662306a36Sopenharmony_ci union ionic_lif_config *lc; 388762306a36Sopenharmony_ci unsigned int ntxqs_per_lif; 388862306a36Sopenharmony_ci unsigned int nrxqs_per_lif; 388962306a36Sopenharmony_ci unsigned int neqs_per_lif; 389062306a36Sopenharmony_ci unsigned int nnqs_per_lif; 389162306a36Sopenharmony_ci unsigned int nxqs, neqs; 389262306a36Sopenharmony_ci unsigned int min_intrs; 389362306a36Sopenharmony_ci int err; 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci /* retrieve basic values from FW */ 389662306a36Sopenharmony_ci lc = &ident->lif.eth.config; 389762306a36Sopenharmony_ci dev_nintrs = le32_to_cpu(ident->dev.nintrs); 389862306a36Sopenharmony_ci neqs_per_lif = le32_to_cpu(ident->lif.rdma.eq_qtype.qid_count); 389962306a36Sopenharmony_ci nnqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_NOTIFYQ]); 390062306a36Sopenharmony_ci ntxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_TXQ]); 390162306a36Sopenharmony_ci nrxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_RXQ]); 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci /* limit values to play nice with kdump */ 390462306a36Sopenharmony_ci if (is_kdump_kernel()) { 390562306a36Sopenharmony_ci dev_nintrs = 2; 390662306a36Sopenharmony_ci neqs_per_lif = 0; 390762306a36Sopenharmony_ci nnqs_per_lif = 0; 390862306a36Sopenharmony_ci ntxqs_per_lif = 1; 390962306a36Sopenharmony_ci nrxqs_per_lif = 1; 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci /* reserve last queue id for hardware timestamping */ 391362306a36Sopenharmony_ci if (lc->features & cpu_to_le64(IONIC_ETH_HW_TIMESTAMP)) { 391462306a36Sopenharmony_ci if (ntxqs_per_lif <= 1 || nrxqs_per_lif <= 1) { 391562306a36Sopenharmony_ci lc->features &= cpu_to_le64(~IONIC_ETH_HW_TIMESTAMP); 391662306a36Sopenharmony_ci } else { 391762306a36Sopenharmony_ci ntxqs_per_lif -= 1; 391862306a36Sopenharmony_ci nrxqs_per_lif -= 1; 391962306a36Sopenharmony_ci } 392062306a36Sopenharmony_ci } 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci nxqs = min(ntxqs_per_lif, nrxqs_per_lif); 392362306a36Sopenharmony_ci nxqs = min(nxqs, num_online_cpus()); 392462306a36Sopenharmony_ci neqs = min(neqs_per_lif, num_online_cpus()); 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_citry_again: 392762306a36Sopenharmony_ci /* interrupt usage: 392862306a36Sopenharmony_ci * 1 for master lif adminq/notifyq 392962306a36Sopenharmony_ci * 1 for each CPU for master lif TxRx queue pairs 393062306a36Sopenharmony_ci * whatever's left is for RDMA queues 393162306a36Sopenharmony_ci */ 393262306a36Sopenharmony_ci nintrs = 1 + nxqs + neqs; 393362306a36Sopenharmony_ci min_intrs = 2; /* adminq + 1 TxRx queue pair */ 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci if (nintrs > dev_nintrs) 393662306a36Sopenharmony_ci goto try_fewer; 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci err = ionic_bus_alloc_irq_vectors(ionic, nintrs); 393962306a36Sopenharmony_ci if (err < 0 && err != -ENOSPC) { 394062306a36Sopenharmony_ci dev_err(ionic->dev, "Can't get intrs from OS: %d\n", err); 394162306a36Sopenharmony_ci return err; 394262306a36Sopenharmony_ci } 394362306a36Sopenharmony_ci if (err == -ENOSPC) 394462306a36Sopenharmony_ci goto try_fewer; 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_ci if (err != nintrs) { 394762306a36Sopenharmony_ci ionic_bus_free_irq_vectors(ionic); 394862306a36Sopenharmony_ci goto try_fewer; 394962306a36Sopenharmony_ci } 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_ci ionic->nnqs_per_lif = nnqs_per_lif; 395262306a36Sopenharmony_ci ionic->neqs_per_lif = neqs; 395362306a36Sopenharmony_ci ionic->ntxqs_per_lif = nxqs; 395462306a36Sopenharmony_ci ionic->nrxqs_per_lif = nxqs; 395562306a36Sopenharmony_ci ionic->nintrs = nintrs; 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci ionic_debugfs_add_sizes(ionic); 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci return 0; 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_citry_fewer: 396262306a36Sopenharmony_ci if (nnqs_per_lif > 1) { 396362306a36Sopenharmony_ci nnqs_per_lif >>= 1; 396462306a36Sopenharmony_ci goto try_again; 396562306a36Sopenharmony_ci } 396662306a36Sopenharmony_ci if (neqs > 1) { 396762306a36Sopenharmony_ci neqs >>= 1; 396862306a36Sopenharmony_ci goto try_again; 396962306a36Sopenharmony_ci } 397062306a36Sopenharmony_ci if (nxqs > 1) { 397162306a36Sopenharmony_ci nxqs >>= 1; 397262306a36Sopenharmony_ci goto try_again; 397362306a36Sopenharmony_ci } 397462306a36Sopenharmony_ci dev_err(ionic->dev, "Can't get minimum %d intrs from OS\n", min_intrs); 397562306a36Sopenharmony_ci return -ENOSPC; 397662306a36Sopenharmony_ci} 3977