Commit 0d5d6045 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-support-for-firmware-upgrade'

Shannon Nelson says:

====================
ionic support for firmware upgrade

The Pensando Distributed Services Card can get firmware upgrades from
the off-host centralized management suite, and can be upgraded without a
host reboot or driver reload.  This patchset sets up the support for fw
upgrade in the Linux driver.

When the upgrade begins, the DSC first brings the link down, then stops
the firmware.  The driver will notice this and quiesce itself by stopping
the queues and releasing DMA resources, then monitoring for firmware to
start back up.  When the upgrade is finished the firmware is restarted
and link is brought up, and the driver rebuilds the queues and restarts
traffic flow.

First we separate the Link state from the netdev state, then reorganize a
few things to prepare for partial tear-down of the queues.  Next we fix
up the state machine so that we take the Tx and Rx queues down and back
up when we get LINK_DOWN and LINK_UP events.  Lastly, we add handling of
the FW reset itself by tearing down the lif internals and rebuilding them
with the new FW setup.

v2: This changes the design from (ab)using the full .ndo_stop and
    .ndo_open routines to getting a better separation between the
    alloc and the init functions so that we can keep our resource
    allocations as long as possible.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ea315c55 c672412f
...@@ -14,11 +14,15 @@ ...@@ -14,11 +14,15 @@
static void ionic_watchdog_cb(struct timer_list *t) static void ionic_watchdog_cb(struct timer_list *t)
{ {
struct ionic *ionic = from_timer(ionic, t, watchdog_timer); struct ionic *ionic = from_timer(ionic, t, watchdog_timer);
int hb;
mod_timer(&ionic->watchdog_timer, mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period)); round_jiffies(jiffies + ionic->watchdog_period));
ionic_heartbeat_check(ionic); hb = ionic_heartbeat_check(ionic);
if (hb >= 0 && ionic->master_lif)
ionic_link_status_check_request(ionic->master_lif);
} }
void ionic_init_devinfo(struct ionic *ionic) void ionic_init_devinfo(struct ionic *ionic)
...@@ -82,6 +86,7 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -82,6 +86,7 @@ int ionic_dev_setup(struct ionic *ionic)
return -EFAULT; return -EFAULT;
} }
idev->last_fw_status = 0xff;
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
mod_timer(&ionic->watchdog_timer, mod_timer(&ionic->watchdog_timer,
...@@ -115,8 +120,43 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -115,8 +120,43 @@ int ionic_heartbeat_check(struct ionic *ionic)
* fw_status != 0xff (bad PCI read) * fw_status != 0xff (bad PCI read)
*/ */
fw_status = ioread8(&idev->dev_info_regs->fw_status); fw_status = ioread8(&idev->dev_info_regs->fw_status);
if (fw_status == 0xff || if (fw_status != 0xff)
!(fw_status & IONIC_FW_STS_F_RUNNING)) fw_status &= IONIC_FW_STS_F_RUNNING; /* use only the run bit */
/* is this a transition? */
if (fw_status != idev->last_fw_status &&
idev->last_fw_status != 0xff) {
struct ionic_lif *lif = ionic->master_lif;
bool trigger = false;
if (!fw_status || fw_status == 0xff) {
dev_info(ionic->dev, "FW stopped %u\n", fw_status);
if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
trigger = true;
} else {
dev_info(ionic->dev, "FW running %u\n", fw_status);
if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))
trigger = true;
}
if (trigger) {
struct ionic_deferred_work *work;
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
dev_err(ionic->dev, "%s OOM\n", __func__);
} else {
work->type = IONIC_DW_TYPE_LIF_RESET;
if (fw_status & IONIC_FW_STS_F_RUNNING &&
fw_status != 0xff)
work->fw_status = 1;
ionic_lif_deferred_enqueue(&lif->deferred, work);
}
}
}
idev->last_fw_status = fw_status;
if (!fw_status || fw_status == 0xff)
return -ENXIO; return -ENXIO;
/* early FW has no heartbeat, else FW will return non-zero */ /* early FW has no heartbeat, else FW will return non-zero */
......
...@@ -132,6 +132,7 @@ struct ionic_dev { ...@@ -132,6 +132,7 @@ struct ionic_dev {
unsigned long last_hb_time; unsigned long last_hb_time;
u32 last_hb; u32 last_hb;
u8 last_fw_status;
u64 __iomem *db_pages; u64 __iomem *db_pages;
dma_addr_t phy_db_pages; dma_addr_t phy_db_pages;
......
...@@ -21,6 +21,12 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode); ...@@ -21,6 +21,12 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode);
static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr); static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr); static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
static void ionic_link_status_check(struct ionic_lif *lif); static void ionic_link_status_check(struct ionic_lif *lif);
static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
static void ionic_lif_set_netdev_info(struct ionic_lif *lif);
static int ionic_start_queues(struct ionic_lif *lif);
static void ionic_stop_queues(struct ionic_lif *lif);
static void ionic_lif_deferred_work(struct work_struct *work) static void ionic_lif_deferred_work(struct work_struct *work)
{ {
...@@ -50,6 +56,12 @@ static void ionic_lif_deferred_work(struct work_struct *work) ...@@ -50,6 +56,12 @@ static void ionic_lif_deferred_work(struct work_struct *work)
case IONIC_DW_TYPE_LINK_STATUS: case IONIC_DW_TYPE_LINK_STATUS:
ionic_link_status_check(lif); ionic_link_status_check(lif);
break; break;
case IONIC_DW_TYPE_LIF_RESET:
if (w->fw_status)
ionic_lif_handle_fw_up(lif);
else
ionic_lif_handle_fw_down(lif);
break;
default: default:
break; break;
} }
...@@ -58,8 +70,8 @@ static void ionic_lif_deferred_work(struct work_struct *work) ...@@ -58,8 +70,8 @@ static void ionic_lif_deferred_work(struct work_struct *work)
} }
} }
static void ionic_lif_deferred_enqueue(struct ionic_deferred *def, void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
struct ionic_deferred_work *work) struct ionic_deferred_work *work)
{ {
spin_lock_bh(&def->lock); spin_lock_bh(&def->lock);
list_add_tail(&work->list, &def->list); list_add_tail(&work->list, &def->list);
...@@ -73,35 +85,42 @@ static void ionic_link_status_check(struct ionic_lif *lif) ...@@ -73,35 +85,42 @@ static void ionic_link_status_check(struct ionic_lif *lif)
u16 link_status; u16 link_status;
bool link_up; bool link_up;
if (!test_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state))
return;
if (lif->ionic->is_mgmt_nic)
return;
link_status = le16_to_cpu(lif->info->status.link_status); link_status = le16_to_cpu(lif->info->status.link_status);
link_up = link_status == IONIC_PORT_OPER_STATUS_UP; link_up = link_status == IONIC_PORT_OPER_STATUS_UP;
/* filter out the no-change cases */
if (link_up == netif_carrier_ok(netdev))
goto link_out;
if (link_up) { if (link_up) {
netdev_info(netdev, "Link up - %d Gbps\n", if (!netif_carrier_ok(netdev)) {
le32_to_cpu(lif->info->status.link_speed) / 1000); u32 link_speed;
if (test_bit(IONIC_LIF_F_UP, lif->state)) { ionic_port_identify(lif->ionic);
netif_tx_wake_all_queues(lif->netdev); link_speed = le32_to_cpu(lif->info->status.link_speed);
netdev_info(netdev, "Link up - %d Gbps\n",
link_speed / 1000);
netif_carrier_on(netdev); netif_carrier_on(netdev);
} }
if (netif_running(lif->netdev))
ionic_start_queues(lif);
} else { } else {
netdev_info(netdev, "Link down\n"); if (netif_carrier_ok(netdev)) {
netdev_info(netdev, "Link down\n");
netif_carrier_off(netdev);
}
/* carrier off first to avoid watchdog timeout */ if (netif_running(lif->netdev))
netif_carrier_off(netdev); ionic_stop_queues(lif);
if (test_bit(IONIC_LIF_F_UP, lif->state))
netif_tx_stop_all_queues(netdev);
} }
link_out:
clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state); clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state);
} }
static void ionic_link_status_check_request(struct ionic_lif *lif) void ionic_link_status_check_request(struct ionic_lif *lif)
{ {
struct ionic_deferred_work *work; struct ionic_deferred_work *work;
...@@ -244,41 +263,20 @@ static int ionic_qcq_disable(struct ionic_qcq *qcq) ...@@ -244,41 +263,20 @@ static int ionic_qcq_disable(struct ionic_qcq *qcq)
return ionic_adminq_post_wait(lif, &ctx); return ionic_adminq_post_wait(lif, &ctx);
} }
static void ionic_lif_quiesce(struct ionic_lif *lif)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.lif_setattr = {
.opcode = IONIC_CMD_LIF_SETATTR,
.attr = IONIC_LIF_ATTR_STATE,
.index = lif->index,
.state = IONIC_LIF_DISABLE
},
};
ionic_adminq_post_wait(lif, &ctx);
}
static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq) static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq)
{ {
struct ionic_dev *idev = &lif->ionic->idev; struct ionic_dev *idev = &lif->ionic->idev;
struct device *dev = lif->ionic->dev;
if (!qcq) if (!qcq)
return; return;
ionic_debugfs_del_qcq(qcq);
if (!(qcq->flags & IONIC_QCQ_F_INITED)) if (!(qcq->flags & IONIC_QCQ_F_INITED))
return; return;
if (qcq->flags & IONIC_QCQ_F_INTR) { if (qcq->flags & IONIC_QCQ_F_INTR) {
ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
IONIC_INTR_MASK_SET); IONIC_INTR_MASK_SET);
irq_set_affinity_hint(qcq->intr.vector, NULL);
devm_free_irq(dev, qcq->intr.vector, &qcq->napi);
netif_napi_del(&qcq->napi); netif_napi_del(&qcq->napi);
qcq->intr.vector = 0;
} }
qcq->flags &= ~IONIC_QCQ_F_INITED; qcq->flags &= ~IONIC_QCQ_F_INITED;
...@@ -291,12 +289,18 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -291,12 +289,18 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
if (!qcq) if (!qcq)
return; return;
ionic_debugfs_del_qcq(qcq);
dma_free_coherent(dev, qcq->total_size, qcq->base, qcq->base_pa); dma_free_coherent(dev, qcq->total_size, qcq->base, qcq->base_pa);
qcq->base = NULL; qcq->base = NULL;
qcq->base_pa = 0; qcq->base_pa = 0;
if (qcq->flags & IONIC_QCQ_F_INTR) if (qcq->flags & IONIC_QCQ_F_INTR) {
irq_set_affinity_hint(qcq->intr.vector, NULL);
devm_free_irq(dev, qcq->intr.vector, &qcq->napi);
qcq->intr.vector = 0;
ionic_intr_free(lif, qcq->intr.index); ionic_intr_free(lif, qcq->intr.index);
}
devm_kfree(dev, qcq->cq.info); devm_kfree(dev, qcq->cq.info);
qcq->cq.info = NULL; qcq->cq.info = NULL;
...@@ -428,6 +432,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -428,6 +432,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
ionic_intr_mask_assert(idev->intr_ctrl, new->intr.index, ionic_intr_mask_assert(idev->intr_ctrl, new->intr.index,
IONIC_INTR_MASK_SET); IONIC_INTR_MASK_SET);
err = ionic_request_irq(lif, new);
if (err) {
netdev_warn(lif->netdev, "irq request failed %d\n", err);
goto err_out_free_intr;
}
new->intr.cpu = cpumask_local_spread(new->intr.index, new->intr.cpu = cpumask_local_spread(new->intr.index,
dev_to_node(dev)); dev_to_node(dev));
if (new->intr.cpu != -1) if (new->intr.cpu != -1)
...@@ -442,13 +452,13 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -442,13 +452,13 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
if (!new->cq.info) { if (!new->cq.info) {
netdev_err(lif->netdev, "Cannot allocate completion queue info\n"); netdev_err(lif->netdev, "Cannot allocate completion queue info\n");
err = -ENOMEM; err = -ENOMEM;
goto err_out_free_intr; goto err_out_free_irq;
} }
err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size); err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size);
if (err) { if (err) {
netdev_err(lif->netdev, "Cannot initialize completion queue\n"); netdev_err(lif->netdev, "Cannot initialize completion queue\n");
goto err_out_free_intr; goto err_out_free_irq;
} }
new->base = dma_alloc_coherent(dev, total_size, &new->base_pa, new->base = dma_alloc_coherent(dev, total_size, &new->base_pa,
...@@ -456,7 +466,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -456,7 +466,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
if (!new->base) { if (!new->base) {
netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n"); netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n");
err = -ENOMEM; err = -ENOMEM;
goto err_out_free_intr; goto err_out_free_irq;
} }
new->total_size = total_size; new->total_size = total_size;
...@@ -482,8 +492,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -482,8 +492,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
return 0; return 0;
err_out_free_irq:
if (flags & IONIC_QCQ_F_INTR)
devm_free_irq(dev, new->intr.vector, &new->napi);
err_out_free_intr: err_out_free_intr:
ionic_intr_free(lif, new->intr.index); if (flags & IONIC_QCQ_F_INTR)
ionic_intr_free(lif, new->intr.index);
err_out: err_out:
dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err); dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err);
return err; return err;
...@@ -505,6 +519,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) ...@@ -505,6 +519,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
0, lif->kern_pid, &lif->adminqcq); 0, lif->kern_pid, &lif->adminqcq);
if (err) if (err)
return err; return err;
ionic_debugfs_add_qcq(lif, lif->adminqcq);
if (lif->ionic->nnqs_per_lif) { if (lif->ionic->nnqs_per_lif) {
flags = IONIC_QCQ_F_NOTIFYQ; flags = IONIC_QCQ_F_NOTIFYQ;
...@@ -515,6 +530,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) ...@@ -515,6 +530,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
0, lif->kern_pid, &lif->notifyqcq); 0, lif->kern_pid, &lif->notifyqcq);
if (err) if (err)
goto err_out_free_adminqcq; goto err_out_free_adminqcq;
ionic_debugfs_add_qcq(lif, lif->notifyqcq);
/* Let the notifyq ride on the adminq interrupt */ /* Let the notifyq ride on the adminq interrupt */
ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq); ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
...@@ -599,6 +615,10 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -599,6 +615,10 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
dev_dbg(dev, "txq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base); dev_dbg(dev, "txq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
dev_dbg(dev, "txq_init.ring_size %d\n", ctx.cmd.q_init.ring_size); dev_dbg(dev, "txq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
q->tail = q->info;
q->head = q->tail;
cq->tail = cq->info;
err = ionic_adminq_post_wait(lif, &ctx); err = ionic_adminq_post_wait(lif, &ctx);
if (err) if (err)
return err; return err;
...@@ -612,8 +632,6 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -612,8 +632,6 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
qcq->flags |= IONIC_QCQ_F_INITED; qcq->flags |= IONIC_QCQ_F_INITED;
ionic_debugfs_add_qcq(lif, qcq);
return 0; return 0;
} }
...@@ -646,6 +664,10 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -646,6 +664,10 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
dev_dbg(dev, "rxq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base); dev_dbg(dev, "rxq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
dev_dbg(dev, "rxq_init.ring_size %d\n", ctx.cmd.q_init.ring_size); dev_dbg(dev, "rxq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
q->tail = q->info;
q->head = q->tail;
cq->tail = cq->info;
err = ionic_adminq_post_wait(lif, &ctx); err = ionic_adminq_post_wait(lif, &ctx);
if (err) if (err)
return err; return err;
...@@ -660,16 +682,8 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -660,16 +682,8 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi, netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi,
NAPI_POLL_WEIGHT); NAPI_POLL_WEIGHT);
err = ionic_request_irq(lif, qcq);
if (err) {
netif_napi_del(&qcq->napi);
return err;
}
qcq->flags |= IONIC_QCQ_F_INITED; qcq->flags |= IONIC_QCQ_F_INITED;
ionic_debugfs_add_qcq(lif, qcq);
return 0; return 0;
} }
...@@ -677,6 +691,7 @@ static bool ionic_notifyq_service(struct ionic_cq *cq, ...@@ -677,6 +691,7 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
struct ionic_cq_info *cq_info) struct ionic_cq_info *cq_info)
{ {
union ionic_notifyq_comp *comp = cq_info->cq_desc; union ionic_notifyq_comp *comp = cq_info->cq_desc;
struct ionic_deferred_work *work;
struct net_device *netdev; struct net_device *netdev;
struct ionic_queue *q; struct ionic_queue *q;
struct ionic_lif *lif; struct ionic_lif *lif;
...@@ -702,11 +717,13 @@ static bool ionic_notifyq_service(struct ionic_cq *cq, ...@@ -702,11 +717,13 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
ionic_link_status_check_request(lif); ionic_link_status_check_request(lif);
break; break;
case IONIC_EVENT_RESET: case IONIC_EVENT_RESET:
netdev_info(netdev, "Notifyq IONIC_EVENT_RESET eid=%lld\n", work = kzalloc(sizeof(*work), GFP_ATOMIC);
eid); if (!work) {
netdev_info(netdev, " reset_code=%d state=%d\n", netdev_err(lif->netdev, "%s OOM\n", __func__);
comp->reset.reset_code, } else {
comp->reset.state); work->type = IONIC_DW_TYPE_LIF_RESET;
ionic_lif_deferred_enqueue(&lif->deferred, work);
}
break; break;
default: default:
netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n", netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
...@@ -1219,7 +1236,8 @@ static int ionic_init_nic_features(struct ionic_lif *lif) ...@@ -1219,7 +1236,8 @@ static int ionic_init_nic_features(struct ionic_lif *lif)
netdev->hw_features |= netdev->hw_enc_features; netdev->hw_features |= netdev->hw_enc_features;
netdev->features |= netdev->hw_features; netdev->features |= netdev->hw_features;
netdev->priv_flags |= IFF_UNICAST_FLT; netdev->priv_flags |= IFF_UNICAST_FLT |
IFF_LIVE_ADDR_CHANGE;
return 0; return 0;
} }
...@@ -1431,13 +1449,20 @@ static void ionic_txrx_disable(struct ionic_lif *lif) ...@@ -1431,13 +1449,20 @@ static void ionic_txrx_disable(struct ionic_lif *lif)
unsigned int i; unsigned int i;
int err; int err;
for (i = 0; i < lif->nxqs; i++) { if (lif->txqcqs) {
err = ionic_qcq_disable(lif->txqcqs[i].qcq); for (i = 0; i < lif->nxqs; i++) {
if (err == -ETIMEDOUT) err = ionic_qcq_disable(lif->txqcqs[i].qcq);
break; if (err == -ETIMEDOUT)
err = ionic_qcq_disable(lif->rxqcqs[i].qcq); break;
if (err == -ETIMEDOUT) }
break; }
if (lif->rxqcqs) {
for (i = 0; i < lif->nxqs; i++) {
err = ionic_qcq_disable(lif->rxqcqs[i].qcq);
if (err == -ETIMEDOUT)
break;
}
} }
} }
...@@ -1445,26 +1470,40 @@ static void ionic_txrx_deinit(struct ionic_lif *lif) ...@@ -1445,26 +1470,40 @@ static void ionic_txrx_deinit(struct ionic_lif *lif)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < lif->nxqs; i++) { if (lif->txqcqs) {
ionic_lif_qcq_deinit(lif, lif->txqcqs[i].qcq); for (i = 0; i < lif->nxqs; i++) {
ionic_tx_flush(&lif->txqcqs[i].qcq->cq); ionic_lif_qcq_deinit(lif, lif->txqcqs[i].qcq);
ionic_tx_flush(&lif->txqcqs[i].qcq->cq);
ionic_tx_empty(&lif->txqcqs[i].qcq->q);
}
}
ionic_lif_qcq_deinit(lif, lif->rxqcqs[i].qcq); if (lif->rxqcqs) {
ionic_rx_flush(&lif->rxqcqs[i].qcq->cq); for (i = 0; i < lif->nxqs; i++) {
ionic_rx_empty(&lif->rxqcqs[i].qcq->q); ionic_lif_qcq_deinit(lif, lif->rxqcqs[i].qcq);
ionic_rx_flush(&lif->rxqcqs[i].qcq->cq);
ionic_rx_empty(&lif->rxqcqs[i].qcq->q);
}
} }
lif->rx_mode = 0;
} }
static void ionic_txrx_free(struct ionic_lif *lif) static void ionic_txrx_free(struct ionic_lif *lif)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < lif->nxqs; i++) { if (lif->txqcqs) {
ionic_qcq_free(lif, lif->txqcqs[i].qcq); for (i = 0; i < lif->nxqs; i++) {
lif->txqcqs[i].qcq = NULL; ionic_qcq_free(lif, lif->txqcqs[i].qcq);
lif->txqcqs[i].qcq = NULL;
}
}
ionic_qcq_free(lif, lif->rxqcqs[i].qcq); if (lif->rxqcqs) {
lif->rxqcqs[i].qcq = NULL; for (i = 0; i < lif->nxqs; i++) {
ionic_qcq_free(lif, lif->rxqcqs[i].qcq);
lif->rxqcqs[i].qcq = NULL;
}
} }
} }
...@@ -1486,6 +1525,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) ...@@ -1486,6 +1525,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)
goto err_out; goto err_out;
lif->txqcqs[i].qcq->stats = lif->txqcqs[i].stats; lif->txqcqs[i].qcq->stats = lif->txqcqs[i].stats;
ionic_debugfs_add_qcq(lif, lif->txqcqs[i].qcq);
} }
flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG | IONIC_QCQ_F_INTR; flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG | IONIC_QCQ_F_INTR;
...@@ -1506,6 +1546,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) ...@@ -1506,6 +1546,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)
lif->rx_coalesce_hw); lif->rx_coalesce_hw);
ionic_link_qcq_interrupts(lif->rxqcqs[i].qcq, ionic_link_qcq_interrupts(lif->rxqcqs[i].qcq,
lif->txqcqs[i].qcq); lif->txqcqs[i].qcq);
ionic_debugfs_add_qcq(lif, lif->rxqcqs[i].qcq);
} }
return 0; return 0;
...@@ -1554,15 +1595,15 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -1554,15 +1595,15 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
int i, err; int i, err;
for (i = 0; i < lif->nxqs; i++) { for (i = 0; i < lif->nxqs; i++) {
err = ionic_qcq_enable(lif->txqcqs[i].qcq); ionic_rx_fill(&lif->rxqcqs[i].qcq->q);
err = ionic_qcq_enable(lif->rxqcqs[i].qcq);
if (err) if (err)
goto err_out; goto err_out;
ionic_rx_fill(&lif->rxqcqs[i].qcq->q); err = ionic_qcq_enable(lif->txqcqs[i].qcq);
err = ionic_qcq_enable(lif->rxqcqs[i].qcq);
if (err) { if (err) {
if (err != -ETIMEDOUT) if (err != -ETIMEDOUT)
ionic_qcq_disable(lif->txqcqs[i].qcq); ionic_qcq_disable(lif->rxqcqs[i].qcq);
goto err_out; goto err_out;
} }
} }
...@@ -1571,10 +1612,10 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -1571,10 +1612,10 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
err_out: err_out:
while (i--) { while (i--) {
err = ionic_qcq_disable(lif->rxqcqs[i].qcq); err = ionic_qcq_disable(lif->txqcqs[i].qcq);
if (err == -ETIMEDOUT) if (err == -ETIMEDOUT)
break; break;
err = ionic_qcq_disable(lif->txqcqs[i].qcq); err = ionic_qcq_disable(lif->rxqcqs[i].qcq);
if (err == -ETIMEDOUT) if (err == -ETIMEDOUT)
break; break;
} }
...@@ -1582,67 +1623,73 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -1582,67 +1623,73 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
return err; return err;
} }
static int ionic_start_queues(struct ionic_lif *lif)
{
int err;
if (test_and_set_bit(IONIC_LIF_F_UP, lif->state))
return 0;
err = ionic_txrx_enable(lif);
if (err) {
clear_bit(IONIC_LIF_F_UP, lif->state);
return err;
}
netif_tx_wake_all_queues(lif->netdev);
return 0;
}
int ionic_open(struct net_device *netdev) int ionic_open(struct net_device *netdev)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
int err; int err;
netif_carrier_off(netdev);
err = ionic_txrx_alloc(lif); err = ionic_txrx_alloc(lif);
if (err) if (err)
return err; return err;
err = ionic_txrx_init(lif); err = ionic_txrx_init(lif);
if (err) if (err)
goto err_txrx_free; goto err_out;
err = ionic_txrx_enable(lif);
if (err)
goto err_txrx_deinit;
netif_set_real_num_tx_queues(netdev, lif->nxqs);
netif_set_real_num_rx_queues(netdev, lif->nxqs);
set_bit(IONIC_LIF_F_UP, lif->state);
ionic_link_status_check_request(lif); /* don't start the queues until we have link */
if (netif_carrier_ok(netdev)) if (netif_carrier_ok(netdev)) {
netif_tx_wake_all_queues(netdev); err = ionic_start_queues(lif);
if (err)
goto err_txrx_deinit;
}
return 0; return 0;
err_txrx_deinit: err_txrx_deinit:
ionic_txrx_deinit(lif); ionic_txrx_deinit(lif);
err_txrx_free: err_out:
ionic_txrx_free(lif); ionic_txrx_free(lif);
return err; return err;
} }
static void ionic_stop_queues(struct ionic_lif *lif)
{
if (!test_and_clear_bit(IONIC_LIF_F_UP, lif->state))
return;
ionic_txrx_disable(lif);
netif_tx_disable(lif->netdev);
}
int ionic_stop(struct net_device *netdev) int ionic_stop(struct net_device *netdev)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
int err = 0;
if (!test_bit(IONIC_LIF_F_UP, lif->state)) { if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
dev_dbg(lif->ionic->dev, "%s: %s state=DOWN\n",
__func__, lif->name);
return 0; return 0;
}
dev_dbg(lif->ionic->dev, "%s: %s state=UP\n", __func__, lif->name);
clear_bit(IONIC_LIF_F_UP, lif->state);
/* carrier off before disabling queues to avoid watchdog timeout */
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
ionic_txrx_disable(lif); ionic_stop_queues(lif);
ionic_lif_quiesce(lif);
ionic_txrx_deinit(lif); ionic_txrx_deinit(lif);
ionic_txrx_free(lif); ionic_txrx_free(lif);
return err; return 0;
} }
static int ionic_get_vf_config(struct net_device *netdev, static int ionic_get_vf_config(struct net_device *netdev,
...@@ -1936,6 +1983,8 @@ static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index ...@@ -1936,6 +1983,8 @@ static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index
ionic_ethtool_set_ops(netdev); ionic_ethtool_set_ops(netdev);
netdev->watchdog_timeo = 2 * HZ; netdev->watchdog_timeo = 2 * HZ;
netif_carrier_off(netdev);
netdev->min_mtu = IONIC_MIN_MTU; netdev->min_mtu = IONIC_MIN_MTU;
netdev->max_mtu = IONIC_MAX_MTU; netdev->max_mtu = IONIC_MAX_MTU;
...@@ -1970,6 +2019,8 @@ static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index ...@@ -1970,6 +2019,8 @@ static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index
goto err_out_free_netdev; goto err_out_free_netdev;
} }
ionic_debugfs_add_lif(lif);
/* allocate queues */ /* allocate queues */
err = ionic_qcqs_alloc(lif); err = ionic_qcqs_alloc(lif);
if (err) if (err)
...@@ -2029,6 +2080,80 @@ static void ionic_lif_reset(struct ionic_lif *lif) ...@@ -2029,6 +2080,80 @@ static void ionic_lif_reset(struct ionic_lif *lif)
mutex_unlock(&lif->ionic->dev_cmd_lock); mutex_unlock(&lif->ionic->dev_cmd_lock);
} }
static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;
if (test_and_set_bit(IONIC_LIF_F_FW_RESET, lif->state))
return;
dev_info(ionic->dev, "FW Down: Stopping LIFs\n");
netif_device_detach(lif->netdev);
if (test_bit(IONIC_LIF_F_UP, lif->state)) {
dev_info(ionic->dev, "Surprise FW stop, stopping queues\n");
ionic_stop_queues(lif);
}
if (netif_running(lif->netdev)) {
ionic_txrx_deinit(lif);
ionic_txrx_free(lif);
}
ionic_lifs_deinit(ionic);
ionic_qcqs_free(lif);
dev_info(ionic->dev, "FW Down: LIFs stopped\n");
}
static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;
int err;
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return;
dev_info(ionic->dev, "FW Up: restarting LIFs\n");
err = ionic_qcqs_alloc(lif);
if (err)
goto err_out;
err = ionic_lifs_init(ionic);
if (err)
goto err_qcqs_free;
if (lif->registered)
ionic_lif_set_netdev_info(lif);
if (netif_running(lif->netdev)) {
err = ionic_txrx_alloc(lif);
if (err)
goto err_lifs_deinit;
err = ionic_txrx_init(lif);
if (err)
goto err_txrx_free;
}
clear_bit(IONIC_LIF_F_FW_RESET, lif->state);
ionic_link_status_check_request(lif);
netif_device_attach(lif->netdev);
dev_info(ionic->dev, "FW Up: LIFs restarted\n");
return;
err_txrx_free:
ionic_txrx_free(lif);
err_lifs_deinit:
ionic_lifs_deinit(ionic);
err_qcqs_free:
ionic_qcqs_free(lif);
err_out:
dev_err(ionic->dev, "FW Up: LIFs restart failed - err %d\n", err);
}
static void ionic_lif_free(struct ionic_lif *lif) static void ionic_lif_free(struct ionic_lif *lif)
{ {
struct device *dev = lif->ionic->dev; struct device *dev = lif->ionic->dev;
...@@ -2041,7 +2166,8 @@ static void ionic_lif_free(struct ionic_lif *lif) ...@@ -2041,7 +2166,8 @@ static void ionic_lif_free(struct ionic_lif *lif)
/* free queues */ /* free queues */
ionic_qcqs_free(lif); ionic_qcqs_free(lif);
ionic_lif_reset(lif); if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
ionic_lif_reset(lif);
/* free lif info */ /* free lif info */
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa); dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
...@@ -2074,17 +2200,19 @@ void ionic_lifs_free(struct ionic *ionic) ...@@ -2074,17 +2200,19 @@ void ionic_lifs_free(struct ionic *ionic)
static void ionic_lif_deinit(struct ionic_lif *lif) static void ionic_lif_deinit(struct ionic_lif *lif)
{ {
if (!test_bit(IONIC_LIF_F_INITED, lif->state)) if (!test_and_clear_bit(IONIC_LIF_F_INITED, lif->state))
return; return;
clear_bit(IONIC_LIF_F_INITED, lif->state); if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
cancel_work_sync(&lif->deferred.work);
cancel_work_sync(&lif->tx_timeout_work);
}
ionic_rx_filters_deinit(lif); ionic_rx_filters_deinit(lif);
if (lif->netdev->features & NETIF_F_RXHASH) if (lif->netdev->features & NETIF_F_RXHASH)
ionic_lif_rss_deinit(lif); ionic_lif_rss_deinit(lif);
napi_disable(&lif->adminqcq->napi); napi_disable(&lif->adminqcq->napi);
netif_napi_del(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq); ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq); ionic_lif_qcq_deinit(lif, lif->adminqcq);
...@@ -2135,13 +2263,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) ...@@ -2135,13 +2263,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif)
netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi, netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi,
NAPI_POLL_WEIGHT); NAPI_POLL_WEIGHT);
err = ionic_request_irq(lif, qcq);
if (err) {
netdev_warn(lif->netdev, "adminq irq request failed %d\n", err);
netif_napi_del(&qcq->napi);
return err;
}
napi_enable(&qcq->napi); napi_enable(&qcq->napi);
if (qcq->flags & IONIC_QCQ_F_INTR) if (qcq->flags & IONIC_QCQ_F_INTR)
...@@ -2150,8 +2271,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) ...@@ -2150,8 +2271,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif)
qcq->flags |= IONIC_QCQ_F_INITED; qcq->flags |= IONIC_QCQ_F_INITED;
ionic_debugfs_add_qcq(lif, qcq);
return 0; return 0;
} }
...@@ -2187,6 +2306,7 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif) ...@@ -2187,6 +2306,7 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif)
if (err) if (err)
return err; return err;
lif->last_eid = 0;
q->hw_type = ctx.comp.q_init.hw_type; q->hw_type = ctx.comp.q_init.hw_type;
q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index); q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
q->dbval = IONIC_DBELL_QID(q->hw_index); q->dbval = IONIC_DBELL_QID(q->hw_index);
...@@ -2199,8 +2319,6 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif) ...@@ -2199,8 +2319,6 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif)
qcq->flags |= IONIC_QCQ_F_INITED; qcq->flags |= IONIC_QCQ_F_INITED;
ionic_debugfs_add_qcq(lif, qcq);
return 0; return 0;
} }
...@@ -2229,8 +2347,8 @@ static int ionic_station_set(struct ionic_lif *lif) ...@@ -2229,8 +2347,8 @@ static int ionic_station_set(struct ionic_lif *lif)
addr.sa_family = AF_INET; addr.sa_family = AF_INET;
err = eth_prepare_mac_addr_change(netdev, &addr); err = eth_prepare_mac_addr_change(netdev, &addr);
if (err) { if (err) {
netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM\n", netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
addr.sa_data); addr.sa_data, err);
return 0; return 0;
} }
...@@ -2254,8 +2372,6 @@ static int ionic_lif_init(struct ionic_lif *lif) ...@@ -2254,8 +2372,6 @@ static int ionic_lif_init(struct ionic_lif *lif)
int dbpage_num; int dbpage_num;
int err; int err;
ionic_debugfs_add_lif(lif);
mutex_lock(&lif->ionic->dev_cmd_lock); mutex_lock(&lif->ionic->dev_cmd_lock);
ionic_dev_cmd_lif_init(idev, lif->index, lif->info_pa); ionic_dev_cmd_lif_init(idev, lif->index, lif->info_pa);
err = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); err = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT);
...@@ -2442,12 +2558,8 @@ void ionic_lifs_unregister(struct ionic *ionic) ...@@ -2442,12 +2558,8 @@ void ionic_lifs_unregister(struct ionic *ionic)
* current model, so don't bother searching the * current model, so don't bother searching the
* ionic->lif for candidates to unregister * ionic->lif for candidates to unregister
*/ */
if (!ionic->master_lif) if (ionic->master_lif &&
return; ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
cancel_work_sync(&ionic->master_lif->deferred.work);
cancel_work_sync(&ionic->master_lif->tx_timeout_work);
if (ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(ionic->master_lif->netdev); unregister_netdev(ionic->master_lif->netdev);
} }
......
...@@ -98,6 +98,7 @@ struct ionic_deferred_work { ...@@ -98,6 +98,7 @@ struct ionic_deferred_work {
union { union {
unsigned int rx_mode; unsigned int rx_mode;
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
u8 fw_status;
}; };
}; };
...@@ -126,6 +127,7 @@ enum ionic_lif_state_flags { ...@@ -126,6 +127,7 @@ enum ionic_lif_state_flags {
IONIC_LIF_F_UP, IONIC_LIF_F_UP,
IONIC_LIF_F_LINK_CHECK_REQUESTED, IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_QUEUE_RESET, IONIC_LIF_F_QUEUE_RESET,
IONIC_LIF_F_FW_RESET,
/* leave this as last */ /* leave this as last */
IONIC_LIF_F_STATE_SIZE IONIC_LIF_F_STATE_SIZE
...@@ -224,6 +226,9 @@ static inline u32 ionic_coal_hw_to_usec(struct ionic *ionic, u32 units) ...@@ -224,6 +226,9 @@ static inline u32 ionic_coal_hw_to_usec(struct ionic *ionic, u32 units)
return (units * div) / mult; return (units * div) / mult;
} }
void ionic_link_status_check_request(struct ionic_lif *lif);
void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
struct ionic_deferred_work *work);
int ionic_lifs_alloc(struct ionic *ionic); int ionic_lifs_alloc(struct ionic *ionic);
void ionic_lifs_free(struct ionic *ionic); void ionic_lifs_free(struct ionic *ionic);
void ionic_lifs_deinit(struct ionic *ionic); void ionic_lifs_deinit(struct ionic *ionic);
......
...@@ -286,9 +286,11 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) ...@@ -286,9 +286,11 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
err = ionic_adminq_post(lif, ctx); err = ionic_adminq_post(lif, ctx);
if (err) { if (err) {
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
netdev_err(netdev, "Posting of %s (%d) failed: %d\n", name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
name, ctx->cmd.cmd.opcode, err); netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
}
return err; return err;
} }
......
...@@ -593,6 +593,22 @@ void ionic_tx_flush(struct ionic_cq *cq) ...@@ -593,6 +593,22 @@ void ionic_tx_flush(struct ionic_cq *cq)
work_done, 0); work_done, 0);
} }
void ionic_tx_empty(struct ionic_queue *q)
{
struct ionic_desc_info *desc_info;
int done = 0;
/* walk the not completed tx entries, if any */
while (q->head != q->tail) {
desc_info = q->tail;
q->tail = desc_info->next;
ionic_tx_clean(q, desc_info, NULL, desc_info->cb_arg);
desc_info->cb = NULL;
desc_info->cb_arg = NULL;
done++;
}
}
static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb)
{ {
int err; int err;
......
...@@ -9,6 +9,7 @@ void ionic_tx_flush(struct ionic_cq *cq); ...@@ -9,6 +9,7 @@ void ionic_tx_flush(struct ionic_cq *cq);
void ionic_rx_fill(struct ionic_queue *q); void ionic_rx_fill(struct ionic_queue *q);
void ionic_rx_empty(struct ionic_queue *q); void ionic_rx_empty(struct ionic_queue *q);
void ionic_tx_empty(struct ionic_queue *q);
int ionic_rx_napi(struct napi_struct *napi, int budget); int ionic_rx_napi(struct napi_struct *napi, int budget);
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment