|
|
|
@ -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_del(struct ionic_lif *lif, const u8 *addr);
|
|
|
|
|
static void ionic_link_status_check(struct ionic_lif *lif);
|
|
|
|
|
static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
|
|
|
|
|
static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
@ -50,6 +56,12 @@ static void ionic_lif_deferred_work(struct work_struct *work)
|
|
|
|
|
case IONIC_DW_TYPE_LINK_STATUS:
|
|
|
|
|
ionic_link_status_check(lif);
|
|
|
|
|
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:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -58,8 +70,8 @@ static void ionic_lif_deferred_work(struct work_struct *work)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
|
|
|
|
|
struct ionic_deferred_work *work)
|
|
|
|
|
void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
|
|
|
|
|
struct ionic_deferred_work *work)
|
|
|
|
|
{
|
|
|
|
|
spin_lock_bh(&def->lock);
|
|
|
|
|
list_add_tail(&work->list, &def->list);
|
|
|
|
@ -73,35 +85,42 @@ static void ionic_link_status_check(struct ionic_lif *lif)
|
|
|
|
|
u16 link_status;
|
|
|
|
|
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_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) {
|
|
|
|
|
netdev_info(netdev, "Link up - %d Gbps\n",
|
|
|
|
|
le32_to_cpu(lif->info->status.link_speed) / 1000);
|
|
|
|
|
if (!netif_carrier_ok(netdev)) {
|
|
|
|
|
u32 link_speed;
|
|
|
|
|
|
|
|
|
|
if (test_bit(IONIC_LIF_F_UP, lif->state)) {
|
|
|
|
|
netif_tx_wake_all_queues(lif->netdev);
|
|
|
|
|
ionic_port_identify(lif->ionic);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
netdev_info(netdev, "Link down\n");
|
|
|
|
|
|
|
|
|
|
/* carrier off first to avoid watchdog timeout */
|
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
if (test_bit(IONIC_LIF_F_UP, lif->state))
|
|
|
|
|
netif_tx_stop_all_queues(netdev);
|
|
|
|
|
if (netif_running(lif->netdev))
|
|
|
|
|
ionic_start_queues(lif);
|
|
|
|
|
} else {
|
|
|
|
|
if (netif_carrier_ok(netdev)) {
|
|
|
|
|
netdev_info(netdev, "Link down\n");
|
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (netif_running(lif->netdev))
|
|
|
|
|
ionic_stop_queues(lif);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
link_out:
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
@ -244,41 +263,20 @@ static int ionic_qcq_disable(struct ionic_qcq *qcq)
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct ionic_dev *idev = &lif->ionic->idev;
|
|
|
|
|
struct device *dev = lif->ionic->dev;
|
|
|
|
|
|
|
|
|
|
if (!qcq)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_del_qcq(qcq);
|
|
|
|
|
|
|
|
|
|
if (!(qcq->flags & IONIC_QCQ_F_INITED))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (qcq->flags & IONIC_QCQ_F_INTR) {
|
|
|
|
|
ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
|
|
|
|
|
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);
|
|
|
|
|
qcq->intr.vector = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qcq->flags &= ~IONIC_QCQ_F_INITED;
|
|
|
|
@ -291,12 +289,18 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
|
|
|
|
|
if (!qcq)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_del_qcq(qcq);
|
|
|
|
|
|
|
|
|
|
dma_free_coherent(dev, qcq->total_size, qcq->base, qcq->base_pa);
|
|
|
|
|
qcq->base = NULL;
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
devm_kfree(dev, qcq->cq.info);
|
|
|
|
|
qcq->cq.info = NULL;
|
|
|
|
@ -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_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,
|
|
|
|
|
dev_to_node(dev));
|
|
|
|
|
if (new->intr.cpu != -1)
|
|
|
|
@ -442,13 +452,13 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
|
|
|
|
|
if (!new->cq.info) {
|
|
|
|
|
netdev_err(lif->netdev, "Cannot allocate completion queue info\n");
|
|
|
|
|
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);
|
|
|
|
|
if (err) {
|
|
|
|
|
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,
|
|
|
|
@ -456,7 +466,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
|
|
|
|
|
if (!new->base) {
|
|
|
|
|
netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n");
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto err_out_free_intr;
|
|
|
|
|
goto err_out_free_irq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new->total_size = total_size;
|
|
|
|
@ -482,8 +492,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
ionic_intr_free(lif, new->intr.index);
|
|
|
|
|
if (flags & IONIC_QCQ_F_INTR)
|
|
|
|
|
ionic_intr_free(lif, new->intr.index);
|
|
|
|
|
err_out:
|
|
|
|
|
dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err);
|
|
|
|
|
return err;
|
|
|
|
@ -505,6 +519,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
|
|
|
|
|
0, lif->kern_pid, &lif->adminqcq);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
ionic_debugfs_add_qcq(lif, lif->adminqcq);
|
|
|
|
|
|
|
|
|
|
if (lif->ionic->nnqs_per_lif) {
|
|
|
|
|
flags = IONIC_QCQ_F_NOTIFYQ;
|
|
|
|
@ -515,6 +530,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
|
|
|
|
|
0, lif->kern_pid, &lif->notifyqcq);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_out_free_adminqcq;
|
|
|
|
|
ionic_debugfs_add_qcq(lif, lif->notifyqcq);
|
|
|
|
|
|
|
|
|
|
/* Let the notifyq ride on the adminq interrupt */
|
|
|
|
|
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)
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
q->tail = q->info;
|
|
|
|
|
q->head = q->tail;
|
|
|
|
|
cq->tail = cq->info;
|
|
|
|
|
|
|
|
|
|
err = ionic_adminq_post_wait(lif, &ctx);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
@ -612,8 +632,6 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
|
|
|
|
|
|
|
|
|
|
qcq->flags |= IONIC_QCQ_F_INITED;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_qcq(lif, qcq);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -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_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);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
@ -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,
|
|
|
|
|
NAPI_POLL_WEIGHT);
|
|
|
|
|
|
|
|
|
|
err = ionic_request_irq(lif, qcq);
|
|
|
|
|
if (err) {
|
|
|
|
|
netif_napi_del(&qcq->napi);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qcq->flags |= IONIC_QCQ_F_INITED;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_qcq(lif, qcq);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -677,6 +691,7 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
|
|
|
|
|
struct ionic_cq_info *cq_info)
|
|
|
|
|
{
|
|
|
|
|
union ionic_notifyq_comp *comp = cq_info->cq_desc;
|
|
|
|
|
struct ionic_deferred_work *work;
|
|
|
|
|
struct net_device *netdev;
|
|
|
|
|
struct ionic_queue *q;
|
|
|
|
|
struct ionic_lif *lif;
|
|
|
|
@ -702,11 +717,13 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
|
|
|
|
|
ionic_link_status_check_request(lif);
|
|
|
|
|
break;
|
|
|
|
|
case IONIC_EVENT_RESET:
|
|
|
|
|
netdev_info(netdev, "Notifyq IONIC_EVENT_RESET eid=%lld\n",
|
|
|
|
|
eid);
|
|
|
|
|
netdev_info(netdev, " reset_code=%d state=%d\n",
|
|
|
|
|
comp->reset.reset_code,
|
|
|
|
|
comp->reset.state);
|
|
|
|
|
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
|
|
|
|
if (!work) {
|
|
|
|
|
netdev_err(lif->netdev, "%s OOM\n", __func__);
|
|
|
|
|
} else {
|
|
|
|
|
work->type = IONIC_DW_TYPE_LIF_RESET;
|
|
|
|
|
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
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)
|
|
|
|
|
netdev->hw_features |= netdev->hw_enc_features;
|
|
|
|
|
netdev->features |= netdev->hw_features;
|
|
|
|
|
|
|
|
|
|
netdev->priv_flags |= IFF_UNICAST_FLT;
|
|
|
|
|
netdev->priv_flags |= IFF_UNICAST_FLT |
|
|
|
|
|
IFF_LIVE_ADDR_CHANGE;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -1431,13 +1449,20 @@ static void ionic_txrx_disable(struct ionic_lif *lif)
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
err = ionic_qcq_disable(lif->txqcqs[i].qcq);
|
|
|
|
|
if (err == -ETIMEDOUT)
|
|
|
|
|
break;
|
|
|
|
|
err = ionic_qcq_disable(lif->rxqcqs[i].qcq);
|
|
|
|
|
if (err == -ETIMEDOUT)
|
|
|
|
|
break;
|
|
|
|
|
if (lif->txqcqs) {
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
err = ionic_qcq_disable(lif->txqcqs[i].qcq);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
ionic_lif_qcq_deinit(lif, lif->txqcqs[i].qcq);
|
|
|
|
|
ionic_tx_flush(&lif->txqcqs[i].qcq->cq);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
if (lif->txqcqs) {
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lif->rxqcqs) {
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
ionic_qcq_free(lif, lif->txqcqs[i].qcq);
|
|
|
|
|
lif->txqcqs[i].qcq = NULL;
|
|
|
|
|
if (lif->txqcqs) {
|
|
|
|
|
for (i = 0; i < lif->nxqs; i++) {
|
|
|
|
|
ionic_qcq_free(lif, lif->txqcqs[i].qcq);
|
|
|
|
|
lif->txqcqs[i].qcq = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ionic_qcq_free(lif, lif->rxqcqs[i].qcq);
|
|
|
|
|
lif->rxqcqs[i].qcq = NULL;
|
|
|
|
|
if (lif->rxqcqs) {
|
|
|
|
|
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)
|
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
@ -1506,6 +1546,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)
|
|
|
|
|
lif->rx_coalesce_hw);
|
|
|
|
|
ionic_link_qcq_interrupts(lif->rxqcqs[i].qcq,
|
|
|
|
|
lif->txqcqs[i].qcq);
|
|
|
|
|
ionic_debugfs_add_qcq(lif, lif->rxqcqs[i].qcq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -1554,15 +1595,15 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
|
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
|
|
ionic_rx_fill(&lif->rxqcqs[i].qcq->q);
|
|
|
|
|
err = ionic_qcq_enable(lif->rxqcqs[i].qcq);
|
|
|
|
|
err = ionic_qcq_enable(lif->txqcqs[i].qcq);
|
|
|
|
|
if (err) {
|
|
|
|
|
if (err != -ETIMEDOUT)
|
|
|
|
|
ionic_qcq_disable(lif->txqcqs[i].qcq);
|
|
|
|
|
ionic_qcq_disable(lif->rxqcqs[i].qcq);
|
|
|
|
|
goto err_out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1571,10 +1612,10 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
|
|
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
|
while (i--) {
|
|
|
|
|
err = ionic_qcq_disable(lif->rxqcqs[i].qcq);
|
|
|
|
|
err = ionic_qcq_disable(lif->txqcqs[i].qcq);
|
|
|
|
|
if (err == -ETIMEDOUT)
|
|
|
|
|
break;
|
|
|
|
|
err = ionic_qcq_disable(lif->txqcqs[i].qcq);
|
|
|
|
|
err = ionic_qcq_disable(lif->rxqcqs[i].qcq);
|
|
|
|
|
if (err == -ETIMEDOUT)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -1582,67 +1623,73 @@ err_out:
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct ionic_lif *lif = netdev_priv(netdev);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
|
|
|
|
|
err = ionic_txrx_alloc(lif);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = ionic_txrx_init(lif);
|
|
|
|
|
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);
|
|
|
|
|
if (netif_carrier_ok(netdev))
|
|
|
|
|
netif_tx_wake_all_queues(netdev);
|
|
|
|
|
/* don't start the queues until we have link */
|
|
|
|
|
if (netif_carrier_ok(netdev)) {
|
|
|
|
|
err = ionic_start_queues(lif);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_txrx_deinit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_txrx_deinit:
|
|
|
|
|
ionic_txrx_deinit(lif);
|
|
|
|
|
err_txrx_free:
|
|
|
|
|
err_out:
|
|
|
|
|
ionic_txrx_free(lif);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct ionic_lif *lif = netdev_priv(netdev);
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
if (!test_bit(IONIC_LIF_F_UP, lif->state)) {
|
|
|
|
|
dev_dbg(lif->ionic->dev, "%s: %s state=DOWN\n",
|
|
|
|
|
__func__, lif->name);
|
|
|
|
|
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
|
|
|
|
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_lif_quiesce(lif);
|
|
|
|
|
ionic_stop_queues(lif);
|
|
|
|
|
ionic_txrx_deinit(lif);
|
|
|
|
|
ionic_txrx_free(lif);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
ionic_ethtool_set_ops(netdev);
|
|
|
|
|
|
|
|
|
|
netdev->watchdog_timeo = 2 * HZ;
|
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
|
|
|
|
|
netdev->min_mtu = IONIC_MIN_MTU;
|
|
|
|
|
netdev->max_mtu = IONIC_MAX_MTU;
|
|
|
|
|
|
|
|
|
@ -1970,6 +2019,8 @@ static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index
|
|
|
|
|
goto err_out_free_netdev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_lif(lif);
|
|
|
|
|
|
|
|
|
|
/* allocate queues */
|
|
|
|
|
err = ionic_qcqs_alloc(lif);
|
|
|
|
|
if (err)
|
|
|
|
@ -2029,6 +2080,80 @@ static void ionic_lif_reset(struct ionic_lif *lif)
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct device *dev = lif->ionic->dev;
|
|
|
|
@ -2041,7 +2166,8 @@ static void ionic_lif_free(struct ionic_lif *lif)
|
|
|
|
|
|
|
|
|
|
/* free queues */
|
|
|
|
|
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 */
|
|
|
|
|
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
|
|
|
|
@ -2074,17 +2200,19 @@ void ionic_lifs_free(struct ionic *ionic)
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
if (lif->netdev->features & NETIF_F_RXHASH)
|
|
|
|
|
ionic_lif_rss_deinit(lif);
|
|
|
|
|
|
|
|
|
|
napi_disable(&lif->adminqcq->napi);
|
|
|
|
|
netif_napi_del(&lif->adminqcq->napi);
|
|
|
|
|
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
|
|
|
|
|
ionic_lif_qcq_deinit(lif, lif->adminqcq);
|
|
|
|
|
|
|
|
|
@ -2135,13 +2263,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif)
|
|
|
|
|
netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi,
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (qcq->flags & IONIC_QCQ_F_INTR)
|
|
|
|
@ -2150,8 +2271,6 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif)
|
|
|
|
|
|
|
|
|
|
qcq->flags |= IONIC_QCQ_F_INITED;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_qcq(lif, qcq);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2187,6 +2306,7 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif)
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
lif->last_eid = 0;
|
|
|
|
|
q->hw_type = ctx.comp.q_init.hw_type;
|
|
|
|
|
q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
|
|
|
|
|
q->dbval = IONIC_DBELL_QID(q->hw_index);
|
|
|
|
@ -2199,8 +2319,6 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif)
|
|
|
|
|
|
|
|
|
|
qcq->flags |= IONIC_QCQ_F_INITED;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_qcq(lif, qcq);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2229,8 +2347,8 @@ static int ionic_station_set(struct ionic_lif *lif)
|
|
|
|
|
addr.sa_family = AF_INET;
|
|
|
|
|
err = eth_prepare_mac_addr_change(netdev, &addr);
|
|
|
|
|
if (err) {
|
|
|
|
|
netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM\n",
|
|
|
|
|
addr.sa_data);
|
|
|
|
|
netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
|
|
|
|
|
addr.sa_data, err);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2254,8 +2372,6 @@ static int ionic_lif_init(struct ionic_lif *lif)
|
|
|
|
|
int dbpage_num;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ionic_debugfs_add_lif(lif);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&lif->ionic->dev_cmd_lock);
|
|
|
|
|
ionic_dev_cmd_lif_init(idev, lif->index, lif->info_pa);
|
|
|
|
|
err = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT);
|
|
|
|
@ -2442,12 +2558,8 @@ void ionic_lifs_unregister(struct ionic *ionic)
|
|
|
|
|
* current model, so don't bother searching the
|
|
|
|
|
* ionic->lif for candidates to unregister
|
|
|
|
|
*/
|
|
|
|
|
if (!ionic->master_lif)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
if (ionic->master_lif &&
|
|
|
|
|
ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
|
|
|
|
|
unregister_netdev(ionic->master_lif->netdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|