From: Hannes Reinecke <hare@suse.de>
To: James Smart <jsmart2021@gmail.com>, linux-scsi@vger.kernel.org
Cc: maier@linux.ibm.com, dwagner@suse.de, bvanassche@acm.org,
Ram Vegesna <ram.vegesna@broadcom.com>
Subject: Re: [PATCH v2 17/32] elx: efct: Hardware queues creation and deletion
Date: Thu, 9 Jan 2020 10:10:59 +0100 [thread overview]
Message-ID: <d14d3759-3672-8d81-7717-5b0ae8246ae6@suse.de> (raw)
In-Reply-To: <20191220223723.26563-18-jsmart2021@gmail.com>
On 12/20/19 11:37 PM, James Smart wrote:
> This patch continues the efct driver population.
>
> This patch adds driver definitions for:
> Routines for queue creation, deletion, and configuration.
> Driven by strings describing configuration topology with
> parsers for the strings.
>
> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
> Signed-off-by: James Smart <jsmart2021@gmail.com>
> ---
> drivers/scsi/elx/efct/efct_hw_queues.c | 1456 ++++++++++++++++++++++++++++++++
> drivers/scsi/elx/efct/efct_hw_queues.h | 67 ++
> 2 files changed, 1523 insertions(+)
> create mode 100644 drivers/scsi/elx/efct/efct_hw_queues.c
> create mode 100644 drivers/scsi/elx/efct/efct_hw_queues.h
>
> diff --git a/drivers/scsi/elx/efct/efct_hw_queues.c b/drivers/scsi/elx/efct/efct_hw_queues.c
> new file mode 100644
> index 000000000000..8bbeef8ad22d
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_hw_queues.c
> @@ -0,0 +1,1456 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
> + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
> + */
> +
> +#include "efct_driver.h"
> +#include "efct_hw.h"
> +#include "efct_hw_queues.h"
> +#include "efct_unsol.h"
> +
> +/**
> + * Given the parsed queue topology spec, the SLI queues are created and
> + * initialized
> + */
> +enum efct_hw_rtn
> +efct_hw_init_queues(struct efct_hw *hw, struct efct_hw_qtop *qtop)
> +{
> + u32 i, j, k;
> + u32 default_lengths[QTOP_LAST], len;
> + u32 rqset_len = 0, rqset_count = 0;
> + u8 rqset_filter_mask = 0;
> + struct hw_eq *eqs[EFCT_HW_MAX_MRQS];
> + struct hw_cq *cqs[EFCT_HW_MAX_MRQS];
> + struct hw_rq *rqs[EFCT_HW_MAX_MRQS];
> + struct efct_hw_qtop_entry *qt, *next_qt;
> + struct efct_hw_mrq mrq;
> + bool use_mrq = false;
> +
> + struct hw_eq *eq = NULL;
> + struct hw_cq *cq = NULL;
> + struct hw_wq *wq = NULL;
> + struct hw_rq *rq = NULL;
> + struct hw_mq *mq = NULL;
> +
> + mrq.num_pairs = 0;
> + default_lengths[QTOP_EQ] = 1024;
> + default_lengths[QTOP_CQ] = hw->num_qentries[SLI_QTYPE_CQ];
> + default_lengths[QTOP_WQ] = hw->num_qentries[SLI_QTYPE_WQ];
> + default_lengths[QTOP_RQ] = hw->num_qentries[SLI_QTYPE_RQ];
> + default_lengths[QTOP_MQ] = EFCT_HW_MQ_DEPTH;
> +
> + hw->eq_count = 0;
> + hw->cq_count = 0;
> + hw->mq_count = 0;
> + hw->wq_count = 0;
> + hw->rq_count = 0;
> + hw->hw_rq_count = 0;
> + INIT_LIST_HEAD(&hw->eq_list);
> +
> + /* If MRQ is requested, Check if it is supported by SLI. */
> + if (hw->config.n_rq > 1 &&
> + !(hw->sli.features & SLI4_REQFEAT_MRQP)) {
> + efc_log_err(hw->os, "MRQ topology not supported by SLI4.\n");
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + if (hw->config.n_rq > 1)
> + use_mrq = true;
> +
> + /* Allocate class WQ pools */
> + for (i = 0; i < ARRAY_SIZE(hw->wq_class_array); i++) {
> + hw->wq_class_array[i] = efct_varray_alloc(hw->os,
> + EFCT_HW_MAX_NUM_WQ);
> + if (!hw->wq_class_array[i]) {
> + efc_log_err(hw->os,
> + "efct_varray_alloc for wq_class failed\n");
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + }
> +
> + /* Allocate per CPU WQ pools */
> + for (i = 0; i < ARRAY_SIZE(hw->wq_cpu_array); i++) {
> + hw->wq_cpu_array[i] = efct_varray_alloc(hw->os,
> + EFCT_HW_MAX_NUM_WQ);
> + if (!hw->wq_cpu_array[i]) {
> + efc_log_err(hw->os,
> + "efct_varray_alloc for wq_class failed\n");
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + }
> +
> + for (i = 0, qt = qtop->entries; i < qtop->inuse_count; i++, qt++) {
> + if (i == qtop->inuse_count - 1)
> + next_qt = NULL;
> + else
> + next_qt = qt + 1;
> +
> + switch (qt->entry) {
> + case QTOP_EQ:
> + len = (qt->len) ? qt->len : default_lengths[QTOP_EQ];
> +
> + if (qt->set_default) {
> + default_lengths[QTOP_EQ] = len;
> + break;
> + }
> +
> + eq = efct_hw_new_eq(hw, len);
> + if (!eq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + break;
> +
> + case QTOP_CQ:
> + len = (qt->len) ? qt->len : default_lengths[QTOP_CQ];
> +
> + if (qt->set_default) {
> + default_lengths[QTOP_CQ] = len;
> + break;
> + }
> +
> + /* If this CQ is for MRQ, then delay the creation */
> + if (!use_mrq || next_qt->entry != QTOP_RQ) {
> + if (!eq)
> + return EFCT_HW_RTN_NO_MEMORY;
> +
> + cq = efct_hw_new_cq(eq, len);
> + if (!cq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + }
> + break;
> +
> + case QTOP_WQ: {
> + len = (qt->len) ? qt->len : default_lengths[QTOP_WQ];
> + if (qt->set_default) {
> + default_lengths[QTOP_WQ] = len;
> + break;
> + }
> +
> + if ((hw->ulp_start + qt->ulp) > hw->ulp_max) {
> + efc_log_err(hw->os,
> + "invalid ULP %d WQ\n", qt->ulp);
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> +
> + wq = efct_hw_new_wq(cq, len,
> + qt->class, hw->ulp_start + qt->ulp);
> + if (!wq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> +
> + /* Place this WQ on the EQ WQ array */
> + if (efct_varray_add(eq->wq_array, wq)) {
> + efc_log_err(hw->os,
> + "QTOP_WQ:EQ efct_varray_add fail\n");
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + /* Place this WQ on the HW class array */
> + if (qt->class < ARRAY_SIZE(hw->wq_class_array)) {
> + if (efct_varray_add
> + (hw->wq_class_array[qt->class], wq)) {
> + efc_log_err(hw->os,
> + "HW wq_class_array efct_varray_add failed\n");
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> + } else {
> + efc_log_err(hw->os,
> + "Invalid class value: %d\n",
> + qt->class);
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + /*
> + * Place this WQ on the per CPU list, asumming that EQs
> + * are mapped to cpu given by the EQ instance modulo
> + * number of CPUs
> + */
> + if (efct_varray_add(hw->wq_cpu_array[eq->instance %
> + num_online_cpus()], wq)) {
> + efc_log_err(hw->os,
> + "HW wq_cpu_array efct_varray_add failed\n");
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + break;
> + }
> + case QTOP_RQ: {
> + len = (qt->len) ? qt->len : EFCT_HW_RQ_ENTRIES_DEF;
> +
> + /*
> + * Use the max supported queue length
> + * if qtop rq len is not a valid value
> + */
> + if (len > default_lengths[QTOP_RQ] ||
> + (len % EFCT_HW_RQ_ENTRIES_MIN)) {
> + efc_log_info(hw->os,
> + "QTOP RQ len %d is invalid. Using max supported RQ len %d\n",
> + len, default_lengths[QTOP_RQ]);
> + len = default_lengths[QTOP_RQ];
> + }
> +
> + if (qt->set_default) {
> + default_lengths[QTOP_RQ] = len;
> + break;
> + }
> +
> + if ((hw->ulp_start + qt->ulp) > hw->ulp_max) {
> + efc_log_err(hw->os,
> + "invalid ULP %d RQ\n", qt->ulp);
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> +
> + if (use_mrq) {
> + k = mrq.num_pairs;
> + mrq.rq_cfg[k].len = len;
> + mrq.rq_cfg[k].ulp = hw->ulp_start + qt->ulp;
> + mrq.rq_cfg[k].filter_mask = qt->filter_mask;
> + mrq.rq_cfg[k].eq = eq;
> + mrq.num_pairs++;
> + } else {
> + rq = efct_hw_new_rq(cq, len,
> + hw->ulp_start + qt->ulp);
> + if (!rq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + rq->filter_mask = qt->filter_mask;
> + }
> + break;
> + }
> +
> + case QTOP_MQ:
> + len = (qt->len) ? qt->len : default_lengths[QTOP_MQ];
> + if (qt->set_default) {
> + default_lengths[QTOP_MQ] = len;
> + break;
> + }
> +
> + if (!cq)
> + return EFCT_HW_RTN_NO_MEMORY;
> +
> + mq = efct_hw_new_mq(cq, len);
> + if (!mq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + break;
> +
> + default:
> + efc_log_crit(hw->os, "Unknown Queue\n");
> + break;
> + }
> + }
> +
> + if (mrq.num_pairs) {
> + /* First create normal RQs. */
> + for (i = 0; i < mrq.num_pairs; i++) {
> + for (j = 0; j < mrq.num_pairs; j++) {
> + if (i != j &&
> + mrq.rq_cfg[i].filter_mask ==
> + mrq.rq_cfg[j].filter_mask) {
> + /* This should be created using set */
> + if (rqset_filter_mask &&
> + rqset_filter_mask !=
> + mrq.rq_cfg[i].filter_mask) {
> + efc_log_crit(hw->os,
> + "Cant create > 1 RQ Set\n");
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + } else if (!rqset_filter_mask) {
> + rqset_filter_mask =
> + mrq.rq_cfg[i].filter_mask;
> + rqset_len = mrq.rq_cfg[i].len;
> + }
> + eqs[rqset_count] = mrq.rq_cfg[i].eq;
> + rqset_count++;
> + break;
> + }
> + }
> + if (j == mrq.num_pairs) {
> + /* Normal RQ */
> + cq = efct_hw_new_cq(mrq.rq_cfg[i].eq,
> + default_lengths[QTOP_CQ]);
> + if (!cq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> +
> + rq = efct_hw_new_rq(cq, mrq.rq_cfg[i].len,
> + mrq.rq_cfg[i].ulp);
> + if (!rq) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_NO_MEMORY;
> + }
> + rq->filter_mask = mrq.rq_cfg[i].filter_mask;
> + }
> + }
> +
> + /* Now create RQ Set */
> + if (rqset_count) {
> + /* Create CQ set */
> + if (efct_hw_new_cq_set(eqs, cqs, rqset_count,
> + default_lengths[QTOP_CQ])) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + /* Create RQ set */
> + if (efct_hw_new_rq_set(cqs, rqs, rqset_count,
> + rqset_len)) {
> + efct_hw_queue_teardown(hw);
> + return EFCT_HW_RTN_ERROR;
> + }
> +
> + for (i = 0; i < rqset_count ; i++) {
> + rqs[i]->filter_mask = rqset_filter_mask;
> + rqs[i]->is_mrq = true;
> + rqs[i]->base_mrq_id = rqs[0]->hdr->id;
> + }
> +
> + hw->hw_mrq_count = rqset_count;
> + }
> + }
> +
> + return EFCT_HW_RTN_SUCCESS;
> +}
> +
> +/* Allocate a new EQ object */
> +struct hw_eq *
> +efct_hw_new_eq(struct efct_hw *hw, u32 entry_count)
> +{
> + struct hw_eq *eq = kmalloc(sizeof(*eq), GFP_KERNEL);
> +
> + if (eq) {
> + memset(eq, 0, sizeof(*eq));
> + eq->type = SLI_QTYPE_EQ;
> + eq->hw = hw;
> + eq->entry_count = entry_count;
> + eq->instance = hw->eq_count++;
> + eq->queue = &hw->eq[eq->instance];
> + INIT_LIST_HEAD(&eq->cq_list);
> +
> + eq->wq_array = efct_varray_alloc(hw->os, EFCT_HW_MAX_NUM_WQ);
> + if (!eq->wq_array) {
> + kfree(eq);
> + eq = NULL;
> + } else {
> + if (sli_queue_alloc(&hw->sli, SLI_QTYPE_EQ,
> + eq->queue,
> + entry_count, NULL)) {
> + efc_log_err(hw->os,
> + "EQ[%d] allocation failure\n",
> + eq->instance);
> + kfree(eq);
> + eq = NULL;
> + } else {
> + sli_eq_modify_delay(&hw->sli, eq->queue,
> + 1, 0, 8);
> + hw->hw_eq[eq->instance] = eq;
> + INIT_LIST_HEAD(&eq->list_entry);
> + list_add_tail(&eq->list_entry, &hw->eq_list);
> + efc_log_debug(hw->os,
> + "create eq[%2d] id %3d len %4d\n",
> + eq->instance, eq->queue->id,
> + eq->entry_count);
> + }
> + }
> + }
> + return eq;
> +}
> +
> +/* Allocate a new CQ object */
> +struct hw_cq *
> +efct_hw_new_cq(struct hw_eq *eq, u32 entry_count)
> +{
> + struct efct_hw *hw = eq->hw;
> + struct hw_cq *cq = kmalloc(sizeof(*cq), GFP_KERNEL);
> +
> + if (cq) {
> + memset(cq, 0, sizeof(*cq));
> + cq->eq = eq;
> + cq->type = SLI_QTYPE_CQ;
> + cq->instance = eq->hw->cq_count++;
> + cq->entry_count = entry_count;
> + cq->queue = &hw->cq[cq->instance];
> +
> + INIT_LIST_HEAD(&cq->q_list);
> +
> + if (sli_queue_alloc(&hw->sli, SLI_QTYPE_CQ, cq->queue,
> + cq->entry_count, eq->queue)) {
> + efc_log_err(hw->os,
> + "CQ[%d] allocation failure len=%d\n",
> + eq->instance,
> + eq->entry_count);
> + kfree(cq);
> + cq = NULL;
> + } else {
> + hw->hw_cq[cq->instance] = cq;
> + INIT_LIST_HEAD(&cq->list_entry);
> + list_add_tail(&cq->list_entry, &eq->cq_list);
> + efc_log_debug(hw->os,
> + "create cq[%2d] id %3d len %4d\n",
> + cq->instance, cq->queue->id,
> + cq->entry_count);
> + }
> + }
> + return cq;
> +}
> +
> +/* Allocate a new CQ Set of objects */
> +u32
> +efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
> + u32 num_cqs, u32 entry_count)
> +{
> + u32 i;
> + struct efct_hw *hw = eqs[0]->hw;
> + struct sli4 *sli4 = &hw->sli;
> + struct hw_cq *cq = NULL;
> + struct sli4_queue *qs[SLI_MAX_CQ_SET_COUNT];
> + struct sli4_queue *assefct[SLI_MAX_CQ_SET_COUNT];
> +
> + /* Initialise CQS pointers to NULL */
> + for (i = 0; i < num_cqs; i++)
> + cqs[i] = NULL;
> +
> + for (i = 0; i < num_cqs; i++) {
> + cq = kmalloc(sizeof(*cq), GFP_KERNEL);
> + if (!cq)
> + goto error;
> +
> + memset(cq, 0, sizeof(*cq));
> + cqs[i] = cq;
> + cq->eq = eqs[i];
> + cq->type = SLI_QTYPE_CQ;
> + cq->instance = hw->cq_count++;
> + cq->entry_count = entry_count;
> + cq->queue = &hw->cq[cq->instance];
> + qs[i] = cq->queue;
> + assefct[i] = eqs[i]->queue;
> + INIT_LIST_HEAD(&cq->q_list);
> + }
> +
> + if (!sli_cq_alloc_set(sli4, qs, num_cqs, entry_count, assefct)) {
> + efc_log_err(hw->os, "Failed to create CQ Set.\n");
> + goto error;
> + }
> +
> + for (i = 0; i < num_cqs; i++) {
> + hw->hw_cq[cqs[i]->instance] = cqs[i];
> + INIT_LIST_HEAD(&cqs[i]->list_entry);
> + list_add_tail(&cqs[i]->list_entry, &cqs[i]->eq->cq_list);
> + }
> +
> + return 0;
> +
> +error:
> + for (i = 0; i < num_cqs; i++) {
> + kfree(cqs[i]);
> + cqs[i] = NULL;
> + }
> + return -1;
> +}
> +
> +/* Allocate a new MQ object */
> +struct hw_mq *
> +efct_hw_new_mq(struct hw_cq *cq, u32 entry_count)
> +{
> + struct efct_hw *hw = cq->eq->hw;
> + struct hw_mq *mq = kmalloc(sizeof(*mq), GFP_KERNEL);
> +
> + if (mq) {
> + memset(mq, 0, sizeof(*mq));
> + mq->cq = cq;
> + mq->type = SLI_QTYPE_MQ;
> + mq->instance = cq->eq->hw->mq_count++;
> + mq->entry_count = entry_count;
> + mq->entry_size = EFCT_HW_MQ_DEPTH;
> + mq->queue = &hw->mq[mq->instance];
> +
> + if (sli_queue_alloc(&hw->sli, SLI_QTYPE_MQ,
> + mq->queue,
> + mq->entry_size,
> + cq->queue)) {
> + efc_log_err(hw->os, "MQ allocation failure\n");
> + kfree(mq);
> + mq = NULL;
> + } else {
> + hw->hw_mq[mq->instance] = mq;
> + INIT_LIST_HEAD(&mq->list_entry);
> + list_add_tail(&mq->list_entry, &cq->q_list);
> + efc_log_debug(hw->os,
> + "create mq[%2d] id %3d len %4d\n",
> + mq->instance, mq->queue->id,
> + mq->entry_count);
> + }
> + }
> + return mq;
> +}
> +
> +/* Allocate a new WQ object */
> +struct hw_wq *
> +efct_hw_new_wq(struct hw_cq *cq, u32 entry_count,
> + u32 class, u32 ulp)
> +{
> + struct efct_hw *hw = cq->eq->hw;
> + struct hw_wq *wq = kmalloc(sizeof(*wq), GFP_KERNEL);
> +
> + if (wq) {
> + memset(wq, 0, sizeof(*wq));
> + wq->hw = cq->eq->hw;
> + wq->cq = cq;
> + wq->type = SLI_QTYPE_WQ;
> + wq->instance = cq->eq->hw->wq_count++;
> + wq->entry_count = entry_count;
> + wq->queue = &hw->wq[wq->instance];
> + wq->ulp = ulp;
> + wq->wqec_set_count = EFCT_HW_WQEC_SET_COUNT;
> + wq->wqec_count = wq->wqec_set_count;
> + wq->free_count = wq->entry_count - 1;
> + wq->class = class;
> + INIT_LIST_HEAD(&wq->pending_list);
> +
> + if (sli_queue_alloc(&hw->sli, SLI_QTYPE_WQ, wq->queue,
> + wq->entry_count, cq->queue)) {
> + efc_log_err(hw->os, "WQ allocation failure\n");
> + kfree(wq);
> + wq = NULL;
> + } else {
> + hw->hw_wq[wq->instance] = wq;
> + INIT_LIST_HEAD(&wq->list_entry);
> + list_add_tail(&wq->list_entry, &cq->q_list);
> + efc_log_debug(hw->os,
> + "create wq[%2d] id %3d len %4d cls %d ulp %d\n",
> + wq->instance, wq->queue->id,
> + wq->entry_count, wq->class, wq->ulp);
> + }
> + }
> + return wq;
> +}
> +
> +/* Allocate an RQ object, which encapsulates 2 SLI queues (for rq pair) */
> +struct hw_rq *
> +efct_hw_new_rq(struct hw_cq *cq, u32 entry_count, u32 ulp)
> +{
> + struct efct_hw *hw = cq->eq->hw;
> + struct hw_rq *rq = kmalloc(sizeof(*rq), GFP_KERNEL);
> +
> + if (rq) {
> + memset(rq, 0, sizeof(*rq));
> + rq->instance = hw->hw_rq_count++;
> + rq->cq = cq;
> + rq->type = SLI_QTYPE_RQ;
> + rq->entry_count = entry_count;
> +
> + /* Create the header RQ */
> + rq->hdr = &hw->rq[hw->rq_count];
> + rq->hdr_entry_size = EFCT_HW_RQ_HEADER_SIZE;
> +
> + if (sli_fc_rq_alloc(&hw->sli, rq->hdr,
> + rq->entry_count,
> + rq->hdr_entry_size,
> + cq->queue,
> + true)) {
> + efc_log_err(hw->os,
> + "RQ allocation failure - header\n");
> + kfree(rq);
> + return NULL;
> + }
> + /* Update hw_rq_lookup[] */
> + hw->hw_rq_lookup[hw->rq_count] = rq->instance;
> + hw->rq_count++;
> + efc_log_debug(hw->os,
> + "create rq[%2d] id %3d len %4d hdr size %4d\n",
> + rq->instance, rq->hdr->id, rq->entry_count,
> + rq->hdr_entry_size);
> +
> + /* Create the default data RQ */
> + rq->data = &hw->rq[hw->rq_count];
> + rq->data_entry_size = hw->config.rq_default_buffer_size;
> +
> + if (sli_fc_rq_alloc(&hw->sli, rq->data,
> + rq->entry_count,
> + rq->data_entry_size,
> + cq->queue,
> + false)) {
> + efc_log_err(hw->os,
> + "RQ allocation failure - first burst\n");
> + kfree(rq);
> + return NULL;
> + }
> + /* Update hw_rq_lookup[] */
> + hw->hw_rq_lookup[hw->rq_count] = rq->instance;
> + hw->rq_count++;
> + efc_log_debug(hw->os,
> + "create rq[%2d] id %3d len %4d data size %4d\n",
> + rq->instance, rq->data->id, rq->entry_count,
> + rq->data_entry_size);
> +
> + hw->hw_rq[rq->instance] = rq;
> + INIT_LIST_HEAD(&rq->list_entry);
> + list_add_tail(&rq->list_entry, &cq->q_list);
> +
> + rq->rq_tracker = kmalloc_array(rq->entry_count,
> + sizeof(struct efc_hw_sequence *),
> + GFP_ATOMIC);
> + if (!rq->rq_tracker)
> + return NULL;
> +
> + memset(rq->rq_tracker, 0,
> + rq->entry_count * sizeof(struct efc_hw_sequence *));
> + }
> + return rq;
> +}
> +
> +/**
> + * Allocate an RQ object SET, where each element in set
> + * encapsulates 2 SLI queues (for rq pair)
> + */
> +u32
> +efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
> + u32 num_rq_pairs, u32 entry_count)
> +{
> + struct efct_hw *hw = cqs[0]->eq->hw;
> + struct hw_rq *rq = NULL;
> + struct sli4_queue *qs[SLI_MAX_RQ_SET_COUNT * 2] = { NULL };
> + u32 i, q_count, size;
> +
> + /* Initialise RQS pointers */
> + for (i = 0; i < num_rq_pairs; i++)
> + rqs[i] = NULL;
> +
> + for (i = 0, q_count = 0; i < num_rq_pairs; i++, q_count += 2) {
> + rq = kmalloc(sizeof(*rq), GFP_KERNEL);
> + if (!rq)
> + goto error;
> +
> + memset(rq, 0, sizeof(*rq));
> + rqs[i] = rq;
> + rq->instance = hw->hw_rq_count++;
> + rq->cq = cqs[i];
> + rq->type = SLI_QTYPE_RQ;
> + rq->entry_count = entry_count;
> +
> + /* Header RQ */
> + rq->hdr = &hw->rq[hw->rq_count];
> + rq->hdr_entry_size = EFCT_HW_RQ_HEADER_SIZE;
> + hw->hw_rq_lookup[hw->rq_count] = rq->instance;
> + hw->rq_count++;
> + qs[q_count] = rq->hdr;
> +
> + /* Data RQ */
> + rq->data = &hw->rq[hw->rq_count];
> + rq->data_entry_size = hw->config.rq_default_buffer_size;
> + hw->hw_rq_lookup[hw->rq_count] = rq->instance;
> + hw->rq_count++;
> + qs[q_count + 1] = rq->data;
> +
> + rq->rq_tracker = NULL;
> + }
> +
> + if (!sli_fc_rq_set_alloc(&hw->sli, num_rq_pairs, qs,
> + cqs[0]->queue->id,
> + rqs[0]->entry_count,
> + rqs[0]->hdr_entry_size,
> + rqs[0]->data_entry_size)) {
> + efc_log_err(hw->os,
> + "RQ Set allocation failure for base CQ=%d\n",
> + cqs[0]->queue->id);
> + goto error;
> + }
> +
> + for (i = 0; i < num_rq_pairs; i++) {
> + hw->hw_rq[rqs[i]->instance] = rqs[i];
> + INIT_LIST_HEAD(&rqs[i]->list_entry);
> + list_add_tail(&rqs[i]->list_entry, &cqs[i]->q_list);
> + size = sizeof(struct efc_hw_sequence *) * rqs[i]->entry_count;
> + rqs[i]->rq_tracker = kmalloc(size, GFP_KERNEL);
> + if (!rqs[i]->rq_tracker)
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + for (i = 0; i < num_rq_pairs; i++) {
> + if (rqs[i]) {
> + kfree(rqs[i]->rq_tracker);
> + kfree(rqs[i]);
> + }
> + }
> +
> + return -1;
> +}
> +
> +void
> +efct_hw_del_eq(struct hw_eq *eq)
> +{
> + if (eq) {
> + struct hw_cq *cq;
> + struct hw_cq *cq_next;
> +
> + list_for_each_entry_safe(cq, cq_next, &eq->cq_list, list_entry)
> + efct_hw_del_cq(cq);
> + efct_varray_free(eq->wq_array);
> + list_del(&eq->list_entry);
> + eq->hw->hw_eq[eq->instance] = NULL;
> + kfree(eq);
> + }
> +}
> +
> +void
> +efct_hw_del_cq(struct hw_cq *cq)
> +{
> + if (cq) {
> + struct hw_q *q;
> + struct hw_q *q_next;
> +
> + list_for_each_entry_safe(q, q_next, &cq->q_list, list_entry) {
> + switch (q->type) {
> + case SLI_QTYPE_MQ:
> + efct_hw_del_mq((struct hw_mq *)q);
> + break;
> + case SLI_QTYPE_WQ:
> + efct_hw_del_wq((struct hw_wq *)q);
> + break;
> + case SLI_QTYPE_RQ:
> + efct_hw_del_rq((struct hw_rq *)q);
> + break;
> + default:
> + break;
> + }
> + }
> + list_del(&cq->list_entry);
> + cq->eq->hw->hw_cq[cq->instance] = NULL;
> + kfree(cq);
> + }
> +}
> +
> +void
> +efct_hw_del_mq(struct hw_mq *mq)
> +{
> + if (mq) {
> + list_del(&mq->list_entry);
> + mq->cq->eq->hw->hw_mq[mq->instance] = NULL;
> + kfree(mq);
> + }
> +}
> +
> +void
> +efct_hw_del_wq(struct hw_wq *wq)
> +{
> + if (wq) {
> + list_del(&wq->list_entry);
> + wq->cq->eq->hw->hw_wq[wq->instance] = NULL;
> + kfree(wq);
> + }
> +}
> +
> +void
> +efct_hw_del_rq(struct hw_rq *rq)
> +{
> + struct efct_hw *hw = NULL;
> +
> + if (rq) {
> + /* Free RQ tracker */
> + kfree(rq->rq_tracker);
> + rq->rq_tracker = NULL;
> + list_del(&rq->list_entry);
> + hw = rq->cq->eq->hw;
> + hw->hw_rq[rq->instance] = NULL;
> + kfree(rq);
> + }
> +}
> +
> +void
> +efct_hw_queue_dump(struct efct_hw *hw)
> +{
> + struct hw_eq *eq;
> + struct hw_cq *cq;
> + struct hw_q *q;
> + struct hw_mq *mq;
> + struct hw_wq *wq;
> + struct hw_rq *rq;
> +
> + list_for_each_entry(eq, &hw->eq_list, list_entry) {
> + efc_log_debug(hw->os, "eq[%d] id %2d\n",
> + eq->instance, eq->queue->id);
> + list_for_each_entry(cq, &eq->cq_list, list_entry) {
> + efc_log_debug(hw->os, "cq[%d] id %2d current\n",
> + cq->instance, cq->queue->id);
> + list_for_each_entry(q, &cq->q_list, list_entry) {
> + switch (q->type) {
> + case SLI_QTYPE_MQ:
> + mq = (struct hw_mq *)q;
> + efc_log_debug(hw->os,
> + " mq[%d] id %2d\n",
> + mq->instance, mq->queue->id);
> + break;
> + case SLI_QTYPE_WQ:
> + wq = (struct hw_wq *)q;
> + efc_log_debug(hw->os,
> + " wq[%d] id %2d\n",
> + wq->instance, wq->queue->id);
> + break;
> + case SLI_QTYPE_RQ:
> + rq = (struct hw_rq *)q;
> + efc_log_debug(hw->os,
> + " rq[%d] hdr id %2d\n",
> + rq->instance, rq->hdr->id);
> + break;
> + default:
> + break;
> + }
> + }
> + }
> + }
> +}
> +
> +void
> +efct_hw_queue_teardown(struct efct_hw *hw)
> +{
> + u32 i;
> + struct hw_eq *eq;
> + struct hw_eq *eq_next;
> +
> + if (hw->eq_list.next) {
> + list_for_each_entry_safe(eq, eq_next, &hw->eq_list,
> + list_entry) {
> + efct_hw_del_eq(eq);
> + }
> + }
> + for (i = 0; i < ARRAY_SIZE(hw->wq_cpu_array); i++) {
> + efct_varray_free(hw->wq_cpu_array[i]);
> + hw->wq_cpu_array[i] = NULL;
> + }
> + for (i = 0; i < ARRAY_SIZE(hw->wq_class_array); i++) {
> + efct_varray_free(hw->wq_class_array[i]);
> + hw->wq_class_array[i] = NULL;
> + }
> +}
> +
> +/**
> + * Allocate a WQ to an IO object
> + *
> + * The next work queue index is used to assign a WQ to an IO.
> + *
> + * If wq_steering is EFCT_HW_WQ_STEERING_CLASS, a WQ from io->wq_class is
> + * selected.
> + *
> + * If wq_steering is EFCT_HW_WQ_STEERING_REQUEST, then a WQ from the EQ that
> + * the IO request came in on is selected.
> + *
> + * If wq_steering is EFCT_HW_WQ_STEERING_CPU, then a WQ associted with the
> + * CPU the request is made on is selected.
> + */
> +struct hw_wq *
> +efct_hw_queue_next_wq(struct efct_hw *hw, struct efct_hw_io *io)
> +{
> + struct hw_eq *eq;
> + struct hw_wq *wq = NULL;
> + u32 cpuidx;
> +
> + switch (io->wq_steering) {
> + case EFCT_HW_WQ_STEERING_CLASS:
> + if (unlikely(io->wq_class >= ARRAY_SIZE(hw->wq_class_array)))
> + break;
> +
> + wq = efct_varray_iter_next(hw->wq_class_array[io->wq_class]);
> + break;
> + case EFCT_HW_WQ_STEERING_REQUEST:
> + eq = io->eq;
> + if (likely(eq))
> + wq = efct_varray_iter_next(eq->wq_array);
> + break;
> + case EFCT_HW_WQ_STEERING_CPU:
> + cpuidx = in_interrupt() ?
> + raw_smp_processor_id() : task_cpu(current);
> +
> + if (likely(cpuidx < ARRAY_SIZE(hw->wq_cpu_array)))
> + wq = efct_varray_iter_next(hw->wq_cpu_array[cpuidx]);
> + break;
> + }
> +
> + if (unlikely(!wq))
> + wq = hw->hw_wq[0];
> +
> + return wq;
> +}
> +
> +u32
> +efct_hw_qtop_eq_count(struct efct_hw *hw)
> +{
> + return hw->qtop->entry_counts[QTOP_EQ];
> +}
> +
> +#define TOKEN_LEN 32
> +
> +/* token types */
> +enum tok_type {
> + TOK_LPAREN = 1,
> + TOK_RPAREN,
> + TOK_COLON,
> + TOK_EQUALS,
> + TOK_QUEUE,
> + TOK_ATTR_NAME,
> + TOK_NUMBER,
> + TOK_NUMBER_VALUE,
> + TOK_NUMBER_LIST,
> +};
> +
> +/* token sub-types */
> +enum tok_subtype {
> + TOK_SUB_EQ = 100,
> + TOK_SUB_CQ,
> + TOK_SUB_RQ,
> + TOK_SUB_MQ,
> + TOK_SUB_WQ,
> + TOK_SUB_LEN,
> + TOK_SUB_CLASS,
> + TOK_SUB_ULP,
> + TOK_SUB_FILTER,
> +};
> +
> +/* convert queue subtype to QTOP entry */
> +static enum efct_hw_qtop_type
> +subtype2qtop(enum tok_subtype q)
> +{
> + switch (q) {
> + case TOK_SUB_EQ: return QTOP_EQ;
> + case TOK_SUB_CQ: return QTOP_CQ;
> + case TOK_SUB_RQ: return QTOP_RQ;
> + case TOK_SUB_MQ: return QTOP_MQ;
> + case TOK_SUB_WQ: return QTOP_WQ;
> + default:
> + break;
> + }
> + return 0;
> +}
> +
> +/* Declare token object */
> +struct tok {
> + enum tok_type type;
> + enum tok_subtype subtype;
> + char string[TOKEN_LEN];
> +};
> +
> +/* Declare token array object */
> +struct tokarray {
> + struct tok *tokens;
> + u32 alloc_count;
> + u32 inuse_count;
> + u32 iter_idx;
> +};
> +
> +/* token match structure */
> +struct tokmatch {
> + char *s;
> + enum tok_type type;
> + enum tok_subtype subtype;
> +};
> +
> +static int
> +idstart(int c)
> +{
> + return isalpha(c) || (c == '_') || (c == '$');
> +}
> +
> +static int
> +idchar(int c)
> +{
> + return idstart(c) || isdigit(c);
> +}
> +
> +/* single character matches */
> +static struct tokmatch cmatches[] = {
> + {"(", TOK_LPAREN},
> + {")", TOK_RPAREN},
> + {":", TOK_COLON},
> + {"=", TOK_EQUALS},
> +};
> +
> +/* identifier match strings */
> +static struct tokmatch smatches[] = {
> + {"eq", TOK_QUEUE, TOK_SUB_EQ},
> + {"cq", TOK_QUEUE, TOK_SUB_CQ},
> + {"rq", TOK_QUEUE, TOK_SUB_RQ},
> + {"mq", TOK_QUEUE, TOK_SUB_MQ},
> + {"wq", TOK_QUEUE, TOK_SUB_WQ},
> + {"len", TOK_ATTR_NAME, TOK_SUB_LEN},
> + {"class", TOK_ATTR_NAME, TOK_SUB_CLASS},
> + {"ulp", TOK_ATTR_NAME, TOK_SUB_ULP},
> + {"filter", TOK_ATTR_NAME, TOK_SUB_FILTER},
> +};
> +
> +/* The string is scanned and the next token is returned */
> +static const char *
> +tokenize(const char *s, struct tok *tok)
> +{
> + u32 i;
> +
> + memset(tok, 0, sizeof(*tok));
> +
> + /* Skip over whitespace */
> + while (*s && isspace(*s))
> + s++;
> +
> + /* Return if nothing left in this string */
> + if (*s == 0)
> + return NULL;
> +
> + /* Look for single character matches */
> + for (i = 0; i < ARRAY_SIZE(cmatches); i++) {
> + if (cmatches[i].s[0] == *s) {
> + tok->type = cmatches[i].type;
> + tok->subtype = cmatches[i].subtype;
> + tok->string[0] = *s++;
> + return s;
> + }
> + }
> +
> + /* Scan for a hex number or decimal */
> + if ((s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) {
> + char *p = tok->string;
> +
> + tok->type = TOK_NUMBER;
> +
> + *p++ = *s++;
> + *p++ = *s++;
> + while ((*s == '.') || isxdigit(*s)) {
> + if ((p - tok->string) < (int)sizeof(tok->string))
> + *p++ = *s;
> + if (*s == ',')
> + tok->type = TOK_NUMBER_LIST;
> + s++;
> + }
> + *p = 0;
> + return s;
> + } else if (isdigit(*s)) {
> + char *p = tok->string;
> +
> + tok->type = TOK_NUMBER;
> + while ((*s == ',') || isdigit(*s)) {
> + if ((p - tok->string) < (int)sizeof(tok->string))
> + *p++ = *s;
> + if (*s == ',')
> + tok->type = TOK_NUMBER_LIST;
> + s++;
> + }
> + *p = 0;
> + return s;
> + }
> +
> + /* Scan for an ID */
> + if (idstart(*s)) {
> + char *p = tok->string;
> +
> + for (*p++ = *s++; idchar(*s); s++) {
> + if ((p - tok->string) < TOKEN_LEN)
> + *p++ = *s;
> + }
> +
> + /* See if this is a $ number value */
> + if (tok->string[0] == '$') {
> + tok->type = TOK_NUMBER_VALUE;
> + } else {
> + /* Look for a string match */
> + for (i = 0; i < ARRAY_SIZE(smatches); i++) {
> + if (strcmp(smatches[i].s, tok->string) == 0) {
> + tok->type = smatches[i].type;
> + tok->subtype = smatches[i].subtype;
> + return s;
> + }
> + }
> + }
> + }
> + return s;
> +}
> +
> +/* convert token type to string */
> +static const char *
> +token_type2s(enum tok_type type)
> +{
> + switch (type) {
> + case TOK_LPAREN:
> + return "TOK_LPAREN";
> + case TOK_RPAREN:
> + return "TOK_RPAREN";
> + case TOK_COLON:
> + return "TOK_COLON";
> + case TOK_EQUALS:
> + return "TOK_EQUALS";
> + case TOK_QUEUE:
> + return "TOK_QUEUE";
> + case TOK_ATTR_NAME:
> + return "TOK_ATTR_NAME";
> + case TOK_NUMBER:
> + return "TOK_NUMBER";
> + case TOK_NUMBER_VALUE:
> + return "TOK_NUMBER_VALUE";
> + case TOK_NUMBER_LIST:
> + return "TOK_NUMBER_LIST";
> + }
> + return "unknown";
> +}
> +
> +/* convert token sub-type to string */
> +static const char *
> +token_subtype2s(enum tok_subtype subtype)
> +{
> + switch (subtype) {
> + case TOK_SUB_EQ:
> + return "TOK_SUB_EQ";
> + case TOK_SUB_CQ:
> + return "TOK_SUB_CQ";
> + case TOK_SUB_RQ:
> + return "TOK_SUB_RQ";
> + case TOK_SUB_MQ:
> + return "TOK_SUB_MQ";
> + case TOK_SUB_WQ:
> + return "TOK_SUB_WQ";
> + case TOK_SUB_LEN:
> + return "TOK_SUB_LEN";
> + case TOK_SUB_CLASS:
> + return "TOK_SUB_CLASS";
> + case TOK_SUB_ULP:
> + return "TOK_SUB_ULP";
> + case TOK_SUB_FILTER:
> + return "TOK_SUB_FILTER";
> + }
> + return "";
> +}
> +
> +/*
> + * A syntax error message is found, the input tokens are dumped up to and
> + * including the token that failed as indicated by the current iterator index.
> + */
> +static void
> +tok_syntax(struct efct_hw *hw, struct tokarray *tokarray)
> +{
> + u32 i;
> + struct tok *tok;
> +
> + efc_log_test(hw->os, "Syntax error:\n");
> +
> + for (i = 0, tok = tokarray->tokens; (i <= tokarray->inuse_count);
> + i++, tok++) {
> + efc_log_test(hw->os, "%s [%2d] %-16s %-16s %s\n",
> + (i == tokarray->iter_idx) ? ">>>" : " ", i,
> + token_type2s(tok->type),
> + token_subtype2s(tok->subtype), tok->string);
> + }
> +}
> +
> +/*
> + * Parses tokens of type TOK_NUMBER and TOK_NUMBER_VALUE, returning a numeric
> + * value
> + */
> +static u32
> +tok_getnumber(struct efct_hw *hw, struct efct_hw_qtop *qtop,
> + struct tok *tok)
> +{
> + u32 rval = 0;
> + u32 num_cpus = num_online_cpus();
> +
> + switch (tok->type) {
> + case TOK_NUMBER_VALUE:
> + if (strcmp(tok->string, "$ncpu") == 0)
> + rval = num_cpus;
> + else if (strcmp(tok->string, "$ncpu1") == 0)
> + rval = num_cpus - 1;
> + else if (strcmp(tok->string, "$nwq") == 0)
> + rval = (hw) ? hw->config.n_wq : 0;
> + else if (strcmp(tok->string, "$maxmrq") == 0)
> + rval = (num_cpus < EFCT_HW_MAX_MRQS)
> + ? num_cpus : EFCT_HW_MAX_MRQS;
> + else if (strcmp(tok->string, "$nulp") == 0)
> + rval = hw->ulp_max - hw->ulp_start + 1;
> + else if ((qtop->rptcount_idx > 0) &&
> + strcmp(tok->string, "$rpt0") == 0)
> + rval = qtop->rptcount[qtop->rptcount_idx - 1];
> + else if ((qtop->rptcount_idx > 1) &&
> + strcmp(tok->string, "$rpt1") == 0)
> + rval = qtop->rptcount[qtop->rptcount_idx - 2];
> + else if ((qtop->rptcount_idx > 2) &&
> + strcmp(tok->string, "$rpt2") == 0)
> + rval = qtop->rptcount[qtop->rptcount_idx - 3];
> + else if ((qtop->rptcount_idx > 3) &&
> + strcmp(tok->string, "$rpt3") == 0)
> + rval = qtop->rptcount[qtop->rptcount_idx - 4];
> + else if (kstrtou32(tok->string, 0, &rval))
> + efc_log_debug(hw->os, "kstrtou32 failed\n");
> +
> + break;
> + case TOK_NUMBER:
> + if (kstrtou32(tok->string, 0, &rval))
> + efc_log_debug(hw->os, "kstrtou32 failed\n");
> + break;
> + default:
> + break;
> + }
> + return rval;
> +}
> +
> +/* The tokens are semantically parsed, to generate QTOP entries */
> +static void
> +parse_sub_filter(struct efct_hw *hw, struct efct_hw_qtop_entry *qt,
> + struct tok *tok, struct efct_hw_qtop *qtop)
> +{
> + u32 mask = 0;
> + char *p;
> + u32 v;
> +
> + if (tok[3].type == TOK_NUMBER_LIST) {
> + mask = 0;
> + p = tok[3].string;
> +
> + while ((p) && *p) {
> + if (kstrtou32(p, 0, &v))
> + efc_log_debug(hw->os, "kstrtou32 failed\n");
> + if (v < 32)
> + mask |= (1U << v);
> +
> + p = strchr(p, ',');
> + if (p)
> + p++;
> + }
> + qt->filter_mask = mask;
> + } else {
> + qt->filter_mask = (1U << tok_getnumber(hw, qtop, &tok[3]));
> + }
> +}
> +
> +/* The tokens are semantically parsed, to generate QTOP entries */
> +static int
> +parse_topology(struct efct_hw *hw, struct tokarray *tokarray,
> + struct efct_hw_qtop *qtop)
> +{
> + struct efct_hw_qtop_entry *qt = qtop->entries + qtop->inuse_count;
> + struct tok *tok;
> + u32 num = 0;
> +
> + for (; (tokarray->iter_idx < tokarray->inuse_count) &&
> + ((tok = &tokarray->tokens[tokarray->iter_idx]) != NULL);) {
> + if (qtop->inuse_count >= qtop->alloc_count)
> + return -1;
> +
> + qt = qtop->entries + qtop->inuse_count;
> +
> + switch (tok[0].type) {
> + case TOK_QUEUE:
> + qt->entry = subtype2qtop(tok[0].subtype);
> + qt->set_default = false;
> + qt->len = 0;
> + qt->class = 0;
> + qtop->inuse_count++;
> +
> + /* Advance current token index */
> + tokarray->iter_idx++;
> +
> + /*
> + * Parse for queue attributes, possibly multiple
> + * instances
> + */
> + while ((tokarray->iter_idx + 4) <=
> + tokarray->inuse_count) {
> + tok = &tokarray->tokens[tokarray->iter_idx];
> + if (tok[0].type == TOK_COLON &&
> + tok[1].type == TOK_ATTR_NAME &&
> + tok[2].type == TOK_EQUALS &&
> + (tok[3].type == TOK_NUMBER ||
> + tok[3].type == TOK_NUMBER_VALUE ||
> + tok[3].type == TOK_NUMBER_LIST)) {
> + num = tok_getnumber(hw, qtop, &tok[3]);
> +
> + switch (tok[1].subtype) {
> + case TOK_SUB_LEN:
> + qt->len = num;
> + break;
> + case TOK_SUB_CLASS:
> + qt->class = num;
> + break;
> + case TOK_SUB_ULP:
> + qt->ulp = num;
> + break;
> + case TOK_SUB_FILTER:
> + parse_sub_filter(hw, qt, tok,
> + qtop);
> + break;
> + default:
> + break;
> + }
> + /* Advance current token index */
> + tokarray->iter_idx += 4;
> + } else {
> + break;
> + }
> + num = 0;
> + }
> + qtop->entry_counts[qt->entry]++;
> + break;
> +
> + case TOK_ATTR_NAME:
> + if (((tokarray->iter_idx + 5) <=
> + tokarray->inuse_count) &&
> + tok[1].type == TOK_COLON &&
> + tok[2].type == TOK_QUEUE &&
> + tok[3].type == TOK_EQUALS &&
> + (tok[4].type == TOK_NUMBER ||
> + tok[4].type == TOK_NUMBER_VALUE)) {
> + qt->entry = subtype2qtop(tok[2].subtype);
> + qt->set_default = true;
> + switch (tok[0].subtype) {
> + case TOK_SUB_LEN:
> + qt->len = tok_getnumber(hw, qtop,
> + &tok[4]);
> + break;
> + case TOK_SUB_CLASS:
> + qt->class = tok_getnumber(hw, qtop,
> + &tok[4]);
> + break;
> + case TOK_SUB_ULP:
> + qt->ulp = tok_getnumber(hw, qtop,
> + &tok[4]);
> + break;
> + default:
> + break;
> + }
> + qtop->inuse_count++;
> + tokarray->iter_idx += 5;
> + } else {
> + tok_syntax(hw, tokarray);
> + return -1;
> + }
> + break;
> +
> + case TOK_NUMBER:
> + case TOK_NUMBER_VALUE: {
> + u32 rpt_count = 1;
> + u32 i;
> + u32 rpt_idx;
> +
> + rpt_count = tok_getnumber(hw, qtop, tok);
> +
> + if (tok[1].type == TOK_LPAREN) {
> + u32 iter_idx_save;
> +
> + tokarray->iter_idx += 2;
> +
> + /* save token array iteration index */
> + iter_idx_save = tokarray->iter_idx;
> +
> + for (i = 0; i < rpt_count; i++) {
> + rpt_idx = qtop->rptcount_idx;
> +
> + if (qtop->rptcount_idx <
> + ARRAY_SIZE(qtop->rptcount)) {
> + qtop->rptcount[rpt_idx + 1] = i;
> + }
> +
> + /* restore token array iteration idx */
> + tokarray->iter_idx = iter_idx_save;
> +
> + /* parse, append to qtop */
> + parse_topology(hw, tokarray, qtop);
> +
> + qtop->rptcount_idx = rpt_idx;
> + }
> + }
> + break;
> + }
> +
> + case TOK_RPAREN:
> + tokarray->iter_idx++;
> + return 0;
> +
> + default:
> + tok_syntax(hw, tokarray);
> + return -1;
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * The queue topology object is allocated, and filled with the results of
> + * parsing the passed in queue topology string
> + */
> +struct efct_hw_qtop *
> +efct_hw_qtop_parse(struct efct_hw *hw, const char *qtop_string)
> +{
> + struct efct_hw_qtop *qtop;
> + struct tokarray tokarray;
> + const char *s;
> +
> + efc_log_debug(hw->os, "queue topology: %s\n", qtop_string);
> +
> + /* Allocate a token array */
> + tokarray.tokens = kmalloc_array(MAX_TOKENS, sizeof(*tokarray.tokens),
> + GFP_KERNEL);
> + if (!tokarray.tokens)
> + return NULL;
> + memset(tokarray.tokens, 0, MAX_TOKENS * sizeof(*tokarray.tokens));
> + tokarray.alloc_count = MAX_TOKENS;
> + tokarray.inuse_count = 0;
> + tokarray.iter_idx = 0;
> +
> + /* Parse the tokens */
> + for (s = qtop_string; (tokarray.inuse_count < tokarray.alloc_count) &&
> + ((s = tokenize(s, &tokarray.tokens[tokarray.inuse_count]))) !=
> + NULL;)
> + tokarray.inuse_count++;
> +
> + /* Allocate a queue topology structure */
> + qtop = kmalloc(sizeof(*qtop), GFP_KERNEL);
> + if (!qtop) {
> + kfree(tokarray.tokens);
> + efc_log_err(hw->os, "malloc qtop failed\n");
> + return NULL;
> + }
> + memset(qtop, 0, sizeof(*qtop));
> + qtop->os = hw->os;
> +
> + /* Allocate queue topology entries */
> + qtop->entries = kzalloc((EFCT_HW_MAX_QTOP_ENTRIES *
> + sizeof(*qtop->entries)), GFP_ATOMIC);
> + if (!qtop->entries) {
> + kfree(qtop);
> + kfree(tokarray.tokens);
> + return NULL;
> + }
> + qtop->alloc_count = EFCT_HW_MAX_QTOP_ENTRIES;
> + qtop->inuse_count = 0;
> +
> + /* Parse the tokens */
> + if (parse_topology(hw, &tokarray, qtop)) {
> + efc_log_err(hw->os, "failed to parse tokens\n");
> + efct_hw_qtop_free(qtop);
> + kfree(tokarray.tokens);
> + return NULL;
> + }
> +
> + /* Free the tokens array */
> + kfree(tokarray.tokens);
> +
> + return qtop;
> +}
> +
> +void
> +efct_hw_qtop_free(struct efct_hw_qtop *qtop)
> +{
> + if (qtop) {
> + kfree(qtop->entries);
> + kfree(qtop);
> + }
> +}
Ah, so here is the magic token parsing.
So please, move the string from the previous patches into this patch to
make more sense of it.
> diff --git a/drivers/scsi/elx/efct/efct_hw_queues.h b/drivers/scsi/elx/efct/efct_hw_queues.h
> new file mode 100644
> index 000000000000..afa43209f823
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_hw_queues.h
> @@ -0,0 +1,67 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
> + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __EFCT_HW_QUEUES_H__
> +#define __EFCT_HW_QUEUES_H__
> +
> +#include "efct_hw.h"
> +
> +#define EFCT_HW_MQ_DEPTH 128
> +
> +enum efct_hw_qtop_type {
> + QTOP_EQ = 0,
> + QTOP_CQ,
> + QTOP_WQ,
> + QTOP_RQ,
> + QTOP_MQ,
> + QTOP_LAST,
> +};
> +
> +struct efct_hw_qtop_entry {
> + enum efct_hw_qtop_type entry;
> + bool set_default;
> + u32 len;
> + u8 class;
> + u8 ulp;
> + u8 filter_mask;
> +};
> +
> +struct efct_hw_mrq {
> + struct rq_config {
> + struct hw_eq *eq;
> + u32 len;
> + u8 class;
> + u8 ulp;
> + u8 filter_mask;
> + } rq_cfg[16];
> + u32 num_pairs;
> +};
> +
> +#define MAX_TOKENS 256
> +#define EFCT_HW_MAX_QTOP_ENTRIES 200
> +
> +struct efct_hw_qtop {
> + void *os;
> + struct efct_hw_qtop_entry *entries;
> + u32 alloc_count;
> + u32 inuse_count;
> + u32 entry_counts[QTOP_LAST];
> + u32 rptcount[10];
> + u32 rptcount_idx;
> +};
> +
> +struct efct_hw_qtop *
> +efct_hw_qtop_parse(struct efct_hw *hw, const char *qtop_string);
> +void efct_hw_qtop_free(struct efct_hw_qtop *qtop);
> +const char *efct_hw_qtop_entry_name(enum efct_hw_qtop_type entry);
> +u32 efct_hw_qtop_eq_count(struct efct_hw *hw);
> +
> +enum efct_hw_rtn
> +efct_hw_init_queues(struct efct_hw *hw, struct efct_hw_qtop *qtop);
> +extern struct hw_wq
> +*efct_hw_queue_next_wq(struct efct_hw *hw, struct efct_hw_io *io);
> +
> +#endif /* __EFCT_HW_QUEUES_H__ */
>
Cheers,
Hannes
--
Dr. Hannes Reinecke Teamlead Storage & Networking
hare@suse.de +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer
next prev parent reply other threads:[~2020-01-09 9:11 UTC|newest]
Thread overview: 77+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-20 22:36 [PATCH v2 00/32] [NEW] efct: Broadcom (Emulex) FC Target driver James Smart
2019-12-20 22:36 ` [PATCH v2 01/32] elx: libefc_sli: SLI-4 register offsets and field definitions James Smart
2020-01-08 7:11 ` Hannes Reinecke
2020-01-09 0:59 ` James Smart
2019-12-20 22:36 ` [PATCH v2 02/32] elx: libefc_sli: SLI Descriptors and Queue entries James Smart
2020-01-08 7:24 ` Hannes Reinecke
2020-01-09 1:00 ` James Smart
2019-12-20 22:36 ` [PATCH v2 03/32] elx: libefc_sli: Data structures and defines for mbox commands James Smart
2020-01-08 7:32 ` Hannes Reinecke
2020-01-09 1:03 ` James Smart
2019-12-20 22:36 ` [PATCH v2 04/32] elx: libefc_sli: queue create/destroy/parse routines James Smart
2020-01-08 7:45 ` Hannes Reinecke
2020-01-09 1:04 ` James Smart
2019-12-20 22:36 ` [PATCH v2 05/32] elx: libefc_sli: Populate and post different WQEs James Smart
2020-01-08 7:54 ` Hannes Reinecke
2020-01-09 1:04 ` James Smart
2019-12-20 22:36 ` [PATCH v2 06/32] elx: libefc_sli: bmbx routines and SLI config commands James Smart
2020-01-08 8:05 ` Hannes Reinecke
2019-12-20 22:36 ` [PATCH v2 07/32] elx: libefc_sli: APIs to setup SLI library James Smart
2020-01-08 8:22 ` Hannes Reinecke
2020-01-09 1:29 ` James Smart
2019-12-20 22:36 ` [PATCH v2 08/32] elx: libefc: Generic state machine framework James Smart
2020-01-09 7:05 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 09/32] elx: libefc: Emulex FC discovery library APIs and definitions James Smart
2020-01-09 7:16 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 10/32] elx: libefc: FC Domain state machine interfaces James Smart
2020-01-09 7:27 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 11/32] elx: libefc: SLI and FC PORT " James Smart
2020-01-09 7:34 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 12/32] elx: libefc: Remote node " James Smart
2020-01-09 8:31 ` Hannes Reinecke
2020-01-09 9:57 ` Daniel Wagner
2019-12-20 22:37 ` [PATCH v2 13/32] elx: libefc: Fabric " James Smart
2020-01-09 8:34 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 14/32] elx: libefc: FC node ELS and state handling James Smart
2020-01-09 8:39 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 15/32] elx: efct: Data structures and defines for hw operations James Smart
2020-01-09 8:41 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 16/32] elx: efct: Driver initialization routines James Smart
2020-01-09 9:01 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 17/32] elx: efct: Hardware queues creation and deletion James Smart
2020-01-09 9:10 ` Hannes Reinecke [this message]
2019-12-20 22:37 ` [PATCH v2 18/32] elx: efct: RQ buffer, memory pool allocation and deallocation APIs James Smart
2020-01-09 9:13 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 19/32] elx: efct: Hardware IO and SGL initialization James Smart
2020-01-09 9:22 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 20/32] elx: efct: Hardware queues processing James Smart
2020-01-09 9:24 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 21/32] elx: efct: Unsolicited FC frame processing routines James Smart
2020-01-09 9:26 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 22/32] elx: efct: Extended link Service IO handling James Smart
2020-01-09 9:38 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 23/32] elx: efct: SCSI IO handling routines James Smart
2020-01-09 9:41 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 24/32] elx: efct: LIO backend interface routines James Smart
2020-01-09 3:56 ` Bart Van Assche
2019-12-20 22:37 ` [PATCH v2 25/32] elx: efct: Hardware IO submission routines James Smart
2020-01-09 9:52 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 26/32] elx: efct: link statistics and SFP data James Smart
2020-01-09 10:12 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 27/32] elx: efct: xport and hardware teardown routines James Smart
2020-01-09 10:14 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 28/32] elx: efct: IO timeout handling routines James Smart
2020-01-09 11:27 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 29/32] elx: efct: Firmware update, async link processing James Smart
2020-01-09 11:45 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 30/32] elx: efct: scsi_transport_fc host interface support James Smart
2020-01-09 11:46 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 31/32] elx: efct: Add Makefile and Kconfig for efct driver James Smart
2019-12-20 23:17 ` Randy Dunlap
2020-01-09 11:47 ` Hannes Reinecke
2019-12-20 22:37 ` [PATCH v2 32/32] elx: efct: Tie into kernel Kconfig and build process James Smart
2019-12-24 7:45 ` kbuild test robot
2019-12-24 21:01 ` Nathan Chancellor
2019-12-25 16:09 ` James Smart
2020-01-09 11:47 ` Hannes Reinecke
2019-12-29 18:27 ` [PATCH v2 00/32] [NEW] efct: Broadcom (Emulex) FC Target driver Sebastian Herbszt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d14d3759-3672-8d81-7717-5b0ae8246ae6@suse.de \
--to=hare@suse.de \
--cc=bvanassche@acm.org \
--cc=dwagner@suse.de \
--cc=jsmart2021@gmail.com \
--cc=linux-scsi@vger.kernel.org \
--cc=maier@linux.ibm.com \
--cc=ram.vegesna@broadcom.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).