ionic: change queue count with no reset

Add to our new ionic_reconfigure_queues() to also be able to change
the number of queues in use, and to change the queue interrupt layout
between split and combined.

Signed-off-by: Shannon Nelson <snelson@pensando.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Shannon Nelson 2020-08-27 16:00:29 -07:00 committed by David S. Miller
parent a34e25ab97
commit 101b40a017
2 changed files with 213 additions and 91 deletions

View File

@ -561,32 +561,15 @@ static void ionic_get_channels(struct net_device *netdev,
}
}
static void ionic_set_queuecount(struct ionic_lif *lif, void *arg)
{
struct ethtool_channels *ch = arg;
if (ch->combined_count) {
lif->nxqs = ch->combined_count;
if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) {
clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
lif->tx_coalesce_usecs = lif->rx_coalesce_usecs;
lif->tx_coalesce_hw = lif->rx_coalesce_hw;
netdev_info(lif->netdev, "Sharing queue interrupts\n");
}
} else {
lif->nxqs = ch->rx_count;
if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) {
set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
netdev_info(lif->netdev, "Splitting queue interrupts\n");
}
}
}
static int ionic_set_channels(struct net_device *netdev,
struct ethtool_channels *ch)
{
struct ionic_lif *lif = netdev_priv(netdev);
int new_cnt;
struct ionic_queue_params qparam;
int max_cnt;
int err;
ionic_init_queue_params(lif, &qparam);
if (ch->rx_count != ch->tx_count) {
netdev_info(netdev, "The rx and tx count must be equal\n");
@ -594,20 +577,63 @@ static int ionic_set_channels(struct net_device *netdev,
}
if (ch->combined_count && ch->rx_count) {
netdev_info(netdev, "Use either combined_count or rx/tx_count, not both\n");
netdev_info(netdev, "Use either combined or rx and tx, not both\n");
return -EINVAL;
}
if (ch->combined_count)
new_cnt = ch->combined_count;
else
new_cnt = ch->rx_count;
max_cnt = lif->ionic->ntxqs_per_lif;
if (ch->combined_count) {
if (ch->combined_count > max_cnt)
return -EINVAL;
if (lif->nxqs != new_cnt)
netdev_info(netdev, "Changing queue count from %d to %d\n",
lif->nxqs, new_cnt);
if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
netdev_info(lif->netdev, "Sharing queue interrupts\n");
else if (ch->combined_count == lif->nxqs)
return 0;
return ionic_reset_queues(lif, ionic_set_queuecount, ch);
if (lif->nxqs != ch->combined_count)
netdev_info(netdev, "Changing queue count from %d to %d\n",
lif->nxqs, ch->combined_count);
qparam.nxqs = ch->combined_count;
qparam.intr_split = 0;
} else {
max_cnt /= 2;
if (ch->rx_count > max_cnt)
return -EINVAL;
if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
netdev_info(lif->netdev, "Splitting queue interrupts\n");
else if (ch->rx_count == lif->nxqs)
return 0;
if (lif->nxqs != ch->rx_count)
netdev_info(netdev, "Changing queue count from %d to %d\n",
lif->nxqs, ch->rx_count);
qparam.nxqs = ch->rx_count;
qparam.intr_split = 1;
}
/* if we're not running, just set the values and return */
if (!netif_running(lif->netdev)) {
lif->nxqs = qparam.nxqs;
if (qparam.intr_split) {
set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
} else {
clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
lif->tx_coalesce_usecs = lif->rx_coalesce_usecs;
lif->tx_coalesce_hw = lif->rx_coalesce_hw;
}
return 0;
}
err = ionic_reconfigure_queues(lif, &qparam);
if (err)
netdev_info(netdev, "Queue reconfiguration failed, changes canceled: %d\n", err);
return err;
}
static u32 ionic_get_priv_flags(struct net_device *netdev)

View File

@ -299,6 +299,18 @@ static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq)
qcq->flags &= ~IONIC_QCQ_F_INITED;
}
static void ionic_qcq_intr_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
if (!(qcq->flags & IONIC_QCQ_F_INTR) || qcq->intr.vector == 0)
return;
irq_set_affinity_hint(qcq->intr.vector, NULL);
devm_free_irq(lif->ionic->dev, qcq->intr.vector, &qcq->napi);
qcq->intr.vector = 0;
ionic_intr_free(lif->ionic, qcq->intr.index);
qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
}
static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
struct device *dev = lif->ionic->dev;
@ -326,12 +338,7 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
qcq->sg_base_pa = 0;
}
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->ionic, qcq->intr.index);
}
ionic_qcq_intr_free(lif, qcq);
if (qcq->cq.info) {
devm_kfree(dev, qcq->cq.info);
@ -341,7 +348,6 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
devm_kfree(dev, qcq->q.info);
qcq->q.info = NULL;
}
devm_kfree(dev, qcq);
}
static void ionic_qcqs_free(struct ionic_lif *lif)
@ -350,11 +356,13 @@ static void ionic_qcqs_free(struct ionic_lif *lif)
if (lif->notifyqcq) {
ionic_qcq_free(lif, lif->notifyqcq);
devm_kfree(dev, lif->notifyqcq);
lif->notifyqcq = NULL;
}
if (lif->adminqcq) {
ionic_qcq_free(lif, lif->adminqcq);
devm_kfree(dev, lif->adminqcq);
lif->adminqcq = NULL;
}
@ -385,6 +393,53 @@ static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq,
n_qcq->intr.index = src_qcq->intr.index;
}
static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
int err;
if (!(qcq->flags & IONIC_QCQ_F_INTR)) {
qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
return 0;
}
err = ionic_intr_alloc(lif, &qcq->intr);
if (err) {
netdev_warn(lif->netdev, "no intr for %s: %d\n",
qcq->q.name, err);
goto err_out;
}
err = ionic_bus_get_irq(lif->ionic, qcq->intr.index);
if (err < 0) {
netdev_warn(lif->netdev, "no vector for %s: %d\n",
qcq->q.name, err);
goto err_out_free_intr;
}
qcq->intr.vector = err;
ionic_intr_mask_assert(lif->ionic->idev.intr_ctrl, qcq->intr.index,
IONIC_INTR_MASK_SET);
err = ionic_request_irq(lif, qcq);
if (err) {
netdev_warn(lif->netdev, "irq request failed %d\n", err);
goto err_out_free_intr;
}
/* try to get the irq on the local numa node first */
qcq->intr.cpu = cpumask_local_spread(qcq->intr.index,
dev_to_node(lif->ionic->dev));
if (qcq->intr.cpu != -1)
cpumask_set_cpu(qcq->intr.cpu, &qcq->intr.affinity_mask);
netdev_dbg(lif->netdev, "%s: Interrupt index %d\n", qcq->q.name, qcq->intr.index);
return 0;
err_out_free_intr:
ionic_intr_free(lif->ionic, qcq->intr.index);
err_out:
return err;
}
static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
unsigned int index,
const char *name, unsigned int flags,
@ -430,39 +485,9 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
goto err_out_free_q_info;
}
if (flags & IONIC_QCQ_F_INTR) {
err = ionic_intr_alloc(lif, &new->intr);
if (err) {
netdev_warn(lif->netdev, "no intr for %s: %d\n",
new->q.name, err);
goto err_out;
}
err = ionic_bus_get_irq(lif->ionic, new->intr.index);
if (err < 0) {
netdev_warn(lif->netdev, "no vector for %s: %d\n",
new->q.name, err);
goto err_out_free_intr;
}
new->intr.vector = err;
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 for %s: %d\n",
new->q.name, err);
goto err_out_free_intr;
}
new->intr.cpu = cpumask_local_spread(new->intr.index,
dev_to_node(dev));
if (new->intr.cpu != -1)
cpumask_set_cpu(new->intr.cpu,
&new->intr.affinity_mask);
} else {
new->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
}
err = ionic_alloc_qcq_interrupt(lif, new);
if (err)
goto err_out;
new->cq.info = devm_kcalloc(dev, num_descs, sizeof(*new->cq.info),
GFP_KERNEL);
@ -528,11 +553,10 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
err_out_free_cq_info:
devm_kfree(dev, new->cq.info);
err_out_free_irq:
if (flags & IONIC_QCQ_F_INTR)
if (flags & IONIC_QCQ_F_INTR) {
devm_free_irq(dev, new->intr.vector, &new->napi);
err_out_free_intr:
if (flags & IONIC_QCQ_F_INTR)
ionic_intr_free(lif->ionic, new->intr.index);
}
err_out_free_q_info:
devm_kfree(dev, new->q.info);
err_out_free_qcq:
@ -635,7 +659,7 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
unsigned int intr_index;
int err;
if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
if (qcq->flags & IONIC_QCQ_F_INTR)
intr_index = qcq->intr.index;
else
intr_index = lif->rxqcqs[q->index]->intr.index;
@ -1539,7 +1563,7 @@ static void ionic_txrx_deinit(struct ionic_lif *lif)
unsigned int i;
if (lif->txqcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < lif->nxqs && lif->txqcqs[i]; i++) {
ionic_lif_qcq_deinit(lif, lif->txqcqs[i]);
ionic_tx_flush(&lif->txqcqs[i]->cq);
ionic_tx_empty(&lif->txqcqs[i]->q);
@ -1547,7 +1571,7 @@ static void ionic_txrx_deinit(struct ionic_lif *lif)
}
if (lif->rxqcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < lif->nxqs && lif->rxqcqs[i]; i++) {
ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]);
ionic_rx_flush(&lif->rxqcqs[i]->cq);
ionic_rx_empty(&lif->rxqcqs[i]->q);
@ -1561,15 +1585,17 @@ static void ionic_txrx_free(struct ionic_lif *lif)
unsigned int i;
if (lif->txqcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < lif->ionic->ntxqs_per_lif && lif->txqcqs[i]; i++) {
ionic_qcq_free(lif, lif->txqcqs[i]);
devm_kfree(lif->ionic->dev, lif->txqcqs[i]);
lif->txqcqs[i] = NULL;
}
}
if (lif->rxqcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) {
ionic_qcq_free(lif, lif->rxqcqs[i]);
devm_kfree(lif->ionic->dev, lif->rxqcqs[i]);
lif->rxqcqs[i] = NULL;
}
}
@ -2088,20 +2114,22 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
unsigned int i;
/* allocate temporary qcq arrays to hold new queue structs */
if (qparam->ntxq_descs != lif->ntxq_descs) {
tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->nxqs,
if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) {
tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->ntxqs_per_lif,
sizeof(struct ionic_qcq *), GFP_KERNEL);
if (!tx_qcqs)
goto err_out;
}
if (qparam->nrxq_descs != lif->nrxq_descs) {
rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->nxqs,
if (qparam->nxqs != lif->nxqs || qparam->nrxq_descs != lif->nrxq_descs) {
rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif,
sizeof(struct ionic_qcq *), GFP_KERNEL);
if (!rx_qcqs)
goto err_out;
}
/* allocate new desc_info and rings with no interrupt flag */
/* allocate new desc_info and rings, but leave the interrupt setup
* until later so as to not mess with the still-running queues
*/
if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 &&
lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz ==
sizeof(struct ionic_txq_sg_desc_v1))
@ -2110,7 +2138,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
sg_desc_sz = sizeof(struct ionic_txq_sg_desc);
if (tx_qcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < qparam->nxqs; i++) {
flags = lif->txqcqs[i]->flags & ~IONIC_QCQ_F_INTR;
err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
qparam->ntxq_descs,
@ -2124,7 +2152,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
}
if (rx_qcqs) {
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < qparam->nxqs; i++) {
flags = lif->rxqcqs[i]->flags & ~IONIC_QCQ_F_INTR;
err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
qparam->nrxq_descs,
@ -2140,33 +2168,90 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
/* stop and clean the queues */
ionic_stop_queues_reconfig(lif);
if (qparam->nxqs != lif->nxqs) {
err = netif_set_real_num_tx_queues(lif->netdev, qparam->nxqs);
if (err)
goto err_out_reinit_unlock;
err = netif_set_real_num_rx_queues(lif->netdev, qparam->nxqs);
if (err) {
netif_set_real_num_tx_queues(lif->netdev, lif->nxqs);
goto err_out_reinit_unlock;
}
}
/* swap new desc_info and rings, keeping existing interrupt config */
if (tx_qcqs) {
lif->ntxq_descs = qparam->ntxq_descs;
for (i = 0; i < lif->nxqs; i++)
for (i = 0; i < qparam->nxqs; i++)
ionic_swap_queues(lif->txqcqs[i], tx_qcqs[i]);
}
if (rx_qcqs) {
lif->nrxq_descs = qparam->nrxq_descs;
for (i = 0; i < lif->nxqs; i++)
for (i = 0; i < qparam->nxqs; i++)
ionic_swap_queues(lif->rxqcqs[i], rx_qcqs[i]);
}
/* re-init the queues */
err = ionic_start_queues_reconfig(lif);
/* if we need to change the interrupt layout, this is the time */
if (qparam->intr_split != test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state) ||
qparam->nxqs != lif->nxqs) {
if (qparam->intr_split) {
set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
} else {
clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
lif->tx_coalesce_usecs = lif->rx_coalesce_usecs;
lif->tx_coalesce_hw = lif->rx_coalesce_hw;
}
/* clear existing interrupt assignments */
for (i = 0; i < lif->ionic->ntxqs_per_lif; i++) {
ionic_qcq_intr_free(lif, lif->txqcqs[i]);
ionic_qcq_intr_free(lif, lif->rxqcqs[i]);
}
/* re-assign the interrupts */
for (i = 0; i < qparam->nxqs; i++) {
lif->rxqcqs[i]->flags |= IONIC_QCQ_F_INTR;
err = ionic_alloc_qcq_interrupt(lif, lif->rxqcqs[i]);
ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
lif->rxqcqs[i]->intr.index,
lif->rx_coalesce_hw);
if (qparam->intr_split) {
lif->txqcqs[i]->flags |= IONIC_QCQ_F_INTR;
err = ionic_alloc_qcq_interrupt(lif, lif->txqcqs[i]);
ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
lif->txqcqs[i]->intr.index,
lif->tx_coalesce_hw);
} else {
lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR;
ionic_link_qcq_interrupts(lif->rxqcqs[i], lif->txqcqs[i]);
}
}
}
swap(lif->nxqs, qparam->nxqs);
err_out_reinit_unlock:
/* re-init the queues, but don't loose an error code */
if (err)
ionic_start_queues_reconfig(lif);
else
err = ionic_start_queues_reconfig(lif);
err_out:
/* free old allocs without cleaning intr */
for (i = 0; i < lif->nxqs; i++) {
for (i = 0; i < qparam->nxqs; i++) {
if (tx_qcqs && tx_qcqs[i]) {
tx_qcqs[i]->flags &= ~IONIC_QCQ_F_INTR;
ionic_qcq_free(lif, tx_qcqs[i]);
devm_kfree(lif->ionic->dev, tx_qcqs[i]);
tx_qcqs[i] = NULL;
}
if (rx_qcqs && rx_qcqs[i]) {
rx_qcqs[i]->flags &= ~IONIC_QCQ_F_INTR;
ionic_qcq_free(lif, rx_qcqs[i]);
devm_kfree(lif->ionic->dev, rx_qcqs[i]);
rx_qcqs[i] = NULL;
}
}
@ -2181,6 +2266,17 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
tx_qcqs = NULL;
}
/* clean the unused dma and info allocations when new set is smaller
* than the full array, but leave the qcq shells in place
*/
for (i = lif->nxqs; i < lif->ionic->ntxqs_per_lif; i++) {
lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR;
ionic_qcq_free(lif, lif->txqcqs[i]);
lif->rxqcqs[i]->flags &= ~IONIC_QCQ_F_INTR;
ionic_qcq_free(lif, lif->rxqcqs[i]);
}
return err;
}