net/ionic: support admin queue
authorAlfredo Cardigliano <cardigliano@ntop.org>
Sun, 19 Jan 2020 15:53:46 +0000 (16:53 +0100)
committerFerruh Yigit <ferruh.yigit@intel.com>
Mon, 20 Jan 2020 17:02:17 +0000 (18:02 +0100)
Add support for the admin queue, which is used for most
of the NIC configurations.

Signed-off-by: Alfredo Cardigliano <cardigliano@ntop.org>
Reviewed-by: Shannon Nelson <snelson@pensando.io>
drivers/net/ionic/ionic.h
drivers/net/ionic/ionic_dev.c
drivers/net/ionic/ionic_dev.h
drivers/net/ionic/ionic_lif.c
drivers/net/ionic/ionic_lif.h
drivers/net/ionic/ionic_main.c

index 964589e..e4592ed 100644 (file)
@@ -56,11 +56,14 @@ struct ionic_adapter {
        uint32_t max_ntxqs_per_lif;
        uint32_t max_nrxqs_per_lif;
        uint32_t nintrs;
+       bool intrs[IONIC_INTR_CTRL_REGS_MAX];
        bool is_mgmt_nic;
        struct rte_pci_device *pci_dev;
        LIST_ENTRY(ionic_adapter) pci_adapters;
 };
 
+int ionic_adminq_check_err(struct ionic_admin_ctx *ctx, bool timeout);
+int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
 int ionic_dev_cmd_wait_check(struct ionic_dev *idev, unsigned long max_wait);
 int ionic_setup(struct ionic_adapter *adapter);
 
index 13e99ac..582c4cc 100644 (file)
@@ -299,6 +299,12 @@ ionic_dev_cmd_lif_reset(struct ionic_dev *idev, uint16_t lif_index)
        ionic_dev_cmd_go(idev, &cmd);
 }
 
+struct ionic_doorbell *
+ionic_db_map(struct ionic_lif *lif, struct ionic_queue *q)
+{
+       return lif->kern_dbpage + q->hw_type;
+}
+
 int
 ionic_db_page_num(struct ionic_lif *lif, int pid)
 {
@@ -312,3 +318,252 @@ ionic_intr_init(struct ionic_dev *idev, struct ionic_intr_info *intr,
        ionic_intr_clean(idev->intr_ctrl, index);
        intr->index = index;
 }
+
+void
+ionic_dev_cmd_adminq_init(struct ionic_dev *idev,
+               struct ionic_qcq *qcq,
+               uint16_t lif_index, uint16_t intr_index)
+{
+       struct ionic_queue *q = &qcq->q;
+       struct ionic_cq *cq = &qcq->cq;
+
+       union ionic_dev_cmd cmd = {
+               .q_init.opcode = IONIC_CMD_Q_INIT,
+               .q_init.lif_index = lif_index,
+               .q_init.type = q->type,
+               .q_init.index = q->index,
+               .q_init.flags = IONIC_QINIT_F_ENA,
+               .q_init.pid = q->pid,
+               .q_init.intr_index = intr_index,
+               .q_init.ring_size = rte_log2_u32(q->num_descs),
+               .q_init.ring_base = q->base_pa,
+               .q_init.cq_ring_base = cq->base_pa,
+       };
+
+       ionic_dev_cmd_go(idev, &cmd);
+}
+
+int
+ionic_cq_init(struct ionic_lif *lif, struct ionic_cq *cq,
+               struct ionic_intr_info *intr,
+               uint32_t num_descs, size_t desc_size)
+{
+       if (desc_size == 0) {
+               IONIC_PRINT(ERR, "Descriptor size is %zu", desc_size);
+               return -EINVAL;
+       }
+
+       if (!rte_is_power_of_2(num_descs) ||
+           num_descs < IONIC_MIN_RING_DESC ||
+           num_descs > IONIC_MAX_RING_DESC) {
+               IONIC_PRINT(ERR, "%u descriptors (min: %u max: %u)",
+                       num_descs, IONIC_MIN_RING_DESC, IONIC_MAX_RING_DESC);
+               return -EINVAL;
+       }
+
+       cq->lif = lif;
+       cq->bound_intr = intr;
+       cq->num_descs = num_descs;
+       cq->desc_size = desc_size;
+       cq->tail_idx = 0;
+       cq->done_color = 1;
+
+       return 0;
+}
+
+void
+ionic_cq_map(struct ionic_cq *cq, void *base, rte_iova_t base_pa)
+{
+       cq->base = base;
+       cq->base_pa = base_pa;
+}
+
+void
+ionic_cq_bind(struct ionic_cq *cq, struct ionic_queue *q)
+{
+       cq->bound_q = q;
+       q->bound_cq = cq;
+}
+
+uint32_t
+ionic_cq_service(struct ionic_cq *cq, uint32_t work_to_do,
+                ionic_cq_cb cb, void *cb_arg)
+{
+       uint32_t work_done = 0;
+
+       if (work_to_do == 0)
+               return 0;
+
+       while (cb(cq, cq->tail_idx, cb_arg)) {
+               cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1);
+               if (cq->tail_idx == 0)
+                       cq->done_color = !cq->done_color;
+
+               if (++work_done == work_to_do)
+                       break;
+       }
+
+       return work_done;
+}
+
+int
+ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev,
+            struct ionic_queue *q, uint32_t index, uint32_t num_descs,
+            size_t desc_size, size_t sg_desc_size, uint32_t pid)
+{
+       uint32_t ring_size;
+
+       if (desc_size == 0 || !rte_is_power_of_2(num_descs))
+               return -EINVAL;
+
+       ring_size = rte_log2_u32(num_descs);
+
+       if (ring_size < 2 || ring_size > 16)
+               return -EINVAL;
+
+       q->lif = lif;
+       q->idev = idev;
+       q->index = index;
+       q->num_descs = num_descs;
+       q->desc_size = desc_size;
+       q->sg_desc_size = sg_desc_size;
+       q->head_idx = 0;
+       q->tail_idx = 0;
+       q->pid = pid;
+
+       return 0;
+}
+
+void
+ionic_q_map(struct ionic_queue *q, void *base, rte_iova_t base_pa)
+{
+       q->base = base;
+       q->base_pa = base_pa;
+}
+
+void
+ionic_q_sg_map(struct ionic_queue *q, void *base, rte_iova_t base_pa)
+{
+       q->sg_base = base;
+       q->sg_base_pa = base_pa;
+}
+
+void
+ionic_q_flush(struct ionic_queue *q)
+{
+       writeq(IONIC_DBELL_QID(q->hw_index) | q->head_idx, q->db);
+}
+
+void
+ionic_q_post(struct ionic_queue *q, bool ring_doorbell, desc_cb cb,
+            void *cb_arg)
+{
+       struct ionic_desc_info *head = &q->info[q->head_idx];
+
+       head->cb = cb;
+       head->cb_arg = cb_arg;
+
+       q->head_idx = (q->head_idx + 1) & (q->num_descs - 1);
+
+       if (ring_doorbell)
+               ionic_q_flush(q);
+}
+
+uint32_t
+ionic_q_space_avail(struct ionic_queue *q)
+{
+       uint32_t avail = q->tail_idx;
+
+       if (q->head_idx >= avail)
+               avail += q->num_descs - q->head_idx - 1;
+       else
+               avail -= q->head_idx + 1;
+
+       return avail;
+}
+
+bool
+ionic_q_has_space(struct ionic_queue *q, uint32_t want)
+{
+       return ionic_q_space_avail(q) >= want;
+}
+
+void
+ionic_q_service(struct ionic_queue *q, uint32_t cq_desc_index,
+               uint32_t stop_index, void *service_cb_arg)
+{
+       struct ionic_desc_info *desc_info;
+       uint32_t curr_q_tail_idx;
+
+       do {
+               desc_info = &q->info[q->tail_idx];
+
+               if (desc_info->cb)
+                       desc_info->cb(q, q->tail_idx, cq_desc_index,
+                               desc_info->cb_arg, service_cb_arg);
+
+               desc_info->cb = NULL;
+               desc_info->cb_arg = NULL;
+
+               curr_q_tail_idx = q->tail_idx;
+               q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
+
+       } while (curr_q_tail_idx != stop_index);
+}
+
+static void
+ionic_adminq_cb(struct ionic_queue *q,
+               uint32_t q_desc_index, uint32_t cq_desc_index,
+               void *cb_arg, void *service_cb_arg __rte_unused)
+{
+       struct ionic_admin_ctx *ctx = cb_arg;
+       struct ionic_admin_comp *cq_desc_base = q->bound_cq->base;
+       struct ionic_admin_comp *cq_desc = &cq_desc_base[cq_desc_index];
+
+       if (unlikely(cq_desc->comp_index != q_desc_index)) {
+               IONIC_WARN_ON(cq_desc->comp_index != q_desc_index);
+               return;
+       }
+
+       memcpy(&ctx->comp, cq_desc, sizeof(*cq_desc));
+
+       ctx->pending_work = false; /* done */
+}
+
+/** ionic_adminq_post - Post an admin command.
+ * @lif:               Handle to lif.
+ * @cmd_ctx:           Api admin command context.
+ *
+ * Post the command to an admin queue in the ethernet driver.  If this command
+ * succeeds, then the command has been posted, but that does not indicate a
+ * completion.  If this command returns success, then the completion callback
+ * will eventually be called.
+ *
+ * Return: zero or negative error status.
+ */
+int
+ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
+{
+       struct ionic_queue *adminq = &lif->adminqcq->q;
+       struct ionic_admin_cmd *q_desc_base = adminq->base;
+       struct ionic_admin_cmd *q_desc;
+       int err = 0;
+
+       rte_spinlock_lock(&lif->adminq_lock);
+
+       if (!ionic_q_has_space(adminq, 1)) {
+               err = -ENOSPC;
+               goto err_out;
+       }
+
+       q_desc = &q_desc_base[adminq->head_idx];
+
+       memcpy(q_desc, &ctx->cmd, sizeof(ctx->cmd));
+
+       ionic_q_post(adminq, true, ionic_adminq_cb, ctx);
+
+err_out:
+       rte_spinlock_unlock(&lif->adminq_lock);
+
+       return err;
+}
index d6f0669..a832ff4 100644 (file)
@@ -9,6 +9,9 @@
 #include "ionic_if.h"
 #include "ionic_regs.h"
 
+#define IONIC_MAX_RING_DESC            32768
+#define IONIC_MIN_RING_DESC            16
+
 #define IONIC_LIFS_MAX                 1024
 
 #define IONIC_DEVCMD_TIMEOUT   30 /* devcmd_timeout */
@@ -121,6 +124,44 @@ struct ionic_dev {
        uint32_t port_info_sz;
 };
 
+struct ionic_queue;
+struct ionic_desc_info;
+
+typedef void (*desc_cb)(struct ionic_queue *q,
+       uint32_t q_desc_index,
+       uint32_t cq_desc_index,
+       void *cb_arg, void *service_cb_arg);
+
+struct ionic_desc_info {
+       desc_cb cb;
+       void *cb_arg;
+};
+
+struct ionic_queue {
+       struct ionic_dev *idev;
+       struct ionic_lif *lif;
+       struct ionic_cq *bound_cq;
+       uint32_t index;
+       uint32_t type;
+       uint32_t hw_index;
+       uint32_t hw_type;
+       void *base;
+       void *sg_base;
+       rte_iova_t base_pa;
+       rte_iova_t sg_base_pa;
+       struct ionic_desc_info *info;
+       uint32_t tail_idx;
+       uint32_t head_idx;
+       uint32_t num_descs;
+       uint32_t desc_size;
+       uint32_t sg_desc_size;
+       uint32_t pid;
+       uint32_t qid;
+       uint32_t qtype;
+       struct ionic_doorbell __iomem *db;
+       void *nop_desc;
+};
+
 #define IONIC_INTR_INDEX_NOT_ASSIGNED  (-1)
 #define IONIC_INTR_NAME_MAX_SZ         (32)
 
@@ -131,8 +172,32 @@ struct ionic_intr_info {
        struct ionic_intr __iomem *ctrl;
 };
 
+struct ionic_cq {
+       struct ionic_lif *lif;
+       struct ionic_queue *bound_q;
+       uint32_t tail_idx;
+       uint32_t num_descs;
+       uint32_t desc_size;
+       bool done_color;
+       void *base;
+       rte_iova_t base_pa;
+       struct ionic_intr_info *bound_intr;
+};
+
+/** ionic_admin_ctx - Admin command context.
+ * @pending_work:      Flag that indicates a completion.
+ * @cmd:               Admin command (64B) to be copied to the queue.
+ * @comp:              Admin completion (16B) copied from the queue.
+ */
+struct ionic_admin_ctx {
+       bool pending_work;
+       union ionic_adminq_cmd cmd;
+       union ionic_adminq_comp comp;
+};
+
 struct ionic_lif;
 struct ionic_adapter;
+struct ionic_qcq;
 
 void ionic_intr_init(struct ionic_dev *idev, struct ionic_intr_info *intr,
        unsigned long index);
@@ -165,7 +230,36 @@ void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, uint8_t type,
 void ionic_dev_cmd_lif_init(struct ionic_dev *idev, uint16_t lif_index,
        rte_iova_t addr);
 void ionic_dev_cmd_lif_reset(struct ionic_dev *idev, uint16_t lif_index);
+void ionic_dev_cmd_adminq_init(struct ionic_dev *idev, struct ionic_qcq *qcq,
+       uint16_t lif_index, uint16_t intr_index);
 
+struct ionic_doorbell __iomem *ionic_db_map(struct ionic_lif *lif,
+       struct ionic_queue *q);
 int ionic_db_page_num(struct ionic_lif *lif, int pid);
 
+int ionic_cq_init(struct ionic_lif *lif, struct ionic_cq *cq,
+       struct ionic_intr_info *intr, uint32_t num_descs,
+       size_t desc_size);
+void ionic_cq_map(struct ionic_cq *cq, void *base, rte_iova_t base_pa);
+void ionic_cq_bind(struct ionic_cq *cq, struct ionic_queue *q);
+typedef bool (*ionic_cq_cb)(struct ionic_cq *cq, uint32_t cq_desc_index,
+               void *cb_arg);
+uint32_t ionic_cq_service(struct ionic_cq *cq, uint32_t work_to_do,
+       ionic_cq_cb cb, void *cb_arg);
+
+int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev,
+       struct ionic_queue *q, uint32_t index, uint32_t num_descs,
+       size_t desc_size, size_t sg_desc_size, uint32_t pid);
+void ionic_q_map(struct ionic_queue *q, void *base, rte_iova_t base_pa);
+void ionic_q_sg_map(struct ionic_queue *q, void *base, rte_iova_t base_pa);
+void ionic_q_flush(struct ionic_queue *q);
+void ionic_q_post(struct ionic_queue *q, bool ring_doorbell, desc_cb cb,
+       void *cb_arg);
+uint32_t ionic_q_space_avail(struct ionic_queue *q);
+bool ionic_q_has_space(struct ionic_queue *q, uint32_t want);
+void ionic_q_service(struct ionic_queue *q, uint32_t cq_desc_index,
+       uint32_t stop_index, void *service_cb_arg);
+
+int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
+
 #endif /* _IONIC_DEV_H_ */
index 9cdab32..78a8c93 100644 (file)
 #include "ionic_lif.h"
 #include "ionic_ethdev.h"
 
+int
+ionic_qcq_enable(struct ionic_qcq *qcq)
+{
+       struct ionic_queue *q = &qcq->q;
+       struct ionic_lif *lif = q->lif;
+       struct ionic_dev *idev = &lif->adapter->idev;
+       struct ionic_admin_ctx ctx = {
+               .pending_work = true,
+               .cmd.q_control = {
+                       .opcode = IONIC_CMD_Q_CONTROL,
+                       .lif_index = lif->index,
+                       .type = q->type,
+                       .index = q->index,
+                       .oper = IONIC_Q_ENABLE,
+               },
+       };
+
+       if (qcq->flags & IONIC_QCQ_F_INTR) {
+               ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+                       IONIC_INTR_MASK_CLEAR);
+       }
+
+       return ionic_adminq_post_wait(lif, &ctx);
+}
+
+int
+ionic_qcq_disable(struct ionic_qcq *qcq)
+{
+       struct ionic_queue *q = &qcq->q;
+       struct ionic_lif *lif = q->lif;
+       struct ionic_dev *idev = &lif->adapter->idev;
+       struct ionic_admin_ctx ctx = {
+               .pending_work = true,
+               .cmd.q_control = {
+                       .opcode = IONIC_CMD_Q_CONTROL,
+                       .lif_index = lif->index,
+                       .type = q->type,
+                       .index = q->index,
+                       .oper = IONIC_Q_DISABLE,
+               },
+       };
+
+       if (qcq->flags & IONIC_QCQ_F_INTR) {
+               ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+                       IONIC_INTR_MASK_SET);
+       }
+
+       return ionic_adminq_post_wait(lif, &ctx);
+}
+
+int
+ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr)
+{
+       struct ionic_adapter *adapter = lif->adapter;
+       struct ionic_dev *idev = &adapter->idev;
+       unsigned long index;
+
+       /*
+        * Note: interrupt handler is called for index = 0 only
+        * (we use interrupts for the notifyq only anyway,
+        * which hash index = 0)
+        */
+
+       for (index = 0; index < adapter->nintrs; index++)
+               if (!adapter->intrs[index])
+                       break;
+
+       if (index == adapter->nintrs)
+               return -ENOSPC;
+
+       adapter->intrs[index] = true;
+
+       ionic_intr_init(idev, intr, index);
+
+       return 0;
+}
+
+void
+ionic_intr_free(struct ionic_lif *lif, struct ionic_intr_info *intr)
+{
+       if (intr->index != IONIC_INTR_INDEX_NOT_ASSIGNED)
+               lif->adapter->intrs[intr->index] = false;
+}
+
+static int
+ionic_qcq_alloc(struct ionic_lif *lif, uint8_t type,
+               uint32_t index,
+               const char *base, uint32_t flags,
+               uint32_t num_descs,
+               uint32_t desc_size,
+               uint32_t cq_desc_size,
+               uint32_t sg_desc_size,
+               uint32_t pid, struct ionic_qcq **qcq)
+{
+       struct ionic_dev *idev = &lif->adapter->idev;
+       struct ionic_qcq *new;
+       uint32_t q_size, cq_size, sg_size, total_size;
+       void *q_base, *cq_base, *sg_base;
+       rte_iova_t q_base_pa = 0;
+       rte_iova_t cq_base_pa = 0;
+       rte_iova_t sg_base_pa = 0;
+       uint32_t socket_id = rte_socket_id();
+       int err;
+
+       *qcq = NULL;
+
+       q_size  = num_descs * desc_size;
+       cq_size = num_descs * cq_desc_size;
+       sg_size = num_descs * sg_desc_size;
+
+       total_size = RTE_ALIGN(q_size, PAGE_SIZE) +
+               RTE_ALIGN(cq_size, PAGE_SIZE);
+       /*
+        * Note: aligning q_size/cq_size is not enough due to cq_base address
+        * aligning as q_base could be not aligned to the page.
+        * Adding PAGE_SIZE.
+        */
+       total_size += PAGE_SIZE;
+
+       if (flags & IONIC_QCQ_F_SG) {
+               total_size += RTE_ALIGN(sg_size, PAGE_SIZE);
+               total_size += PAGE_SIZE;
+       }
+
+       new = rte_zmalloc("ionic", sizeof(*new), 0);
+       if (!new) {
+               IONIC_PRINT(ERR, "Cannot allocate queue structure");
+               return -ENOMEM;
+       }
+
+       new->lif = lif;
+       new->flags = flags;
+
+       new->q.info = rte_zmalloc("ionic", sizeof(*new->q.info) * num_descs, 0);
+       if (!new->q.info) {
+               IONIC_PRINT(ERR, "Cannot allocate queue info");
+               return -ENOMEM;
+       }
+
+       new->q.type = type;
+
+       err = ionic_q_init(lif, idev, &new->q, index, num_descs,
+               desc_size, sg_desc_size, pid);
+       if (err) {
+               IONIC_PRINT(ERR, "Queue initialization failed");
+               return err;
+       }
+
+       if (flags & IONIC_QCQ_F_INTR) {
+               err = ionic_intr_alloc(lif, &new->intr);
+               if (err)
+                       return err;
+
+               ionic_intr_mask_assert(idev->intr_ctrl, new->intr.index,
+                       IONIC_INTR_MASK_SET);
+       } else {
+               new->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
+       }
+
+       err = ionic_cq_init(lif, &new->cq, &new->intr,
+               num_descs, cq_desc_size);
+       if (err) {
+               IONIC_PRINT(ERR, "Completion queue initialization failed");
+               goto err_out_free_intr;
+       }
+
+       new->base_z = rte_eth_dma_zone_reserve(lif->eth_dev,
+               base /* name */, index /* queue_idx */,
+               total_size, IONIC_ALIGN, socket_id);
+
+       if (!new->base_z) {
+               IONIC_PRINT(ERR, "Cannot reserve queue DMA memory");
+               err = -ENOMEM;
+               goto err_out_free_intr;
+       }
+
+       new->base = new->base_z->addr;
+       new->base_pa = new->base_z->iova;
+       new->total_size = total_size;
+
+       q_base = new->base;
+       q_base_pa = new->base_pa;
+
+       cq_base = (void *)RTE_ALIGN((uintptr_t)q_base + q_size, PAGE_SIZE);
+       cq_base_pa = RTE_ALIGN(q_base_pa + q_size, PAGE_SIZE);
+
+       if (flags & IONIC_QCQ_F_SG) {
+               sg_base = (void *)RTE_ALIGN((uintptr_t)cq_base + cq_size,
+                       PAGE_SIZE);
+               sg_base_pa = RTE_ALIGN(cq_base_pa + cq_size, PAGE_SIZE);
+               ionic_q_sg_map(&new->q, sg_base, sg_base_pa);
+       }
+
+       IONIC_PRINT(DEBUG, "Q-Base-PA = %ju CQ-Base-PA = %ju "
+               "SG-base-PA = %ju",
+               q_base_pa, cq_base_pa, sg_base_pa);
+
+       ionic_q_map(&new->q, q_base, q_base_pa);
+       ionic_cq_map(&new->cq, cq_base, cq_base_pa);
+       ionic_cq_bind(&new->cq, &new->q);
+
+       *qcq = new;
+
+       return 0;
+
+err_out_free_intr:
+       if (flags & IONIC_QCQ_F_INTR)
+               ionic_intr_free(lif, &new->intr);
+
+       return err;
+}
+
+void
+ionic_qcq_free(struct ionic_qcq *qcq)
+{
+       if (qcq->base_z) {
+               qcq->base = NULL;
+               qcq->base_pa = 0;
+               rte_memzone_free(qcq->base_z);
+               qcq->base_z = NULL;
+       }
+
+       if (qcq->q.info) {
+               rte_free(qcq->q.info);
+               qcq->q.info = NULL;
+       }
+
+       rte_free(qcq);
+}
+
+static int
+ionic_admin_qcq_alloc(struct ionic_lif *lif)
+{
+       uint32_t flags;
+       int err = -ENOMEM;
+
+       flags = 0;
+       err = ionic_qcq_alloc(lif, IONIC_QTYPE_ADMINQ, 0, "admin", flags,
+               IONIC_ADMINQ_LENGTH,
+               sizeof(struct ionic_admin_cmd),
+               sizeof(struct ionic_admin_comp),
+               0,
+               lif->kern_pid, &lif->adminqcq);
+
+       if (err)
+               return err;
+
+       return 0;
+}
+
 static void *
 ionic_bus_map_dbpage(struct ionic_adapter *adapter, int page_num)
 {
@@ -27,11 +277,15 @@ ionic_lif_alloc(struct ionic_lif *lif)
        struct ionic_adapter *adapter = lif->adapter;
        uint32_t socket_id = rte_socket_id();
        int dbpage_num;
+       int err;
 
        snprintf(lif->name, sizeof(lif->name), "lif%u", lif->index);
 
        IONIC_PRINT(DEBUG, "Allocating Lif Info");
 
+       rte_spinlock_init(&lif->adminq_lock);
+       rte_spinlock_init(&lif->adminq_service_lock);
+
        lif->kern_pid = 0;
 
        dbpage_num = ionic_db_page_num(lif, 0);
@@ -42,6 +296,16 @@ ionic_lif_alloc(struct ionic_lif *lif)
                return -ENOMEM;
        }
 
+       IONIC_PRINT(DEBUG, "Allocating Admin Queue");
+
+       err = ionic_admin_qcq_alloc(lif);
+       if (err) {
+               IONIC_PRINT(ERR, "Cannot allocate admin queue");
+               return err;
+       }
+
+       IONIC_PRINT(DEBUG, "Allocating Lif Info");
+
        lif->info_sz = RTE_ALIGN(sizeof(*lif->info), PAGE_SIZE);
 
        lif->info_z = rte_eth_dma_zone_reserve(lif->eth_dev,
@@ -61,12 +325,93 @@ ionic_lif_alloc(struct ionic_lif *lif)
 void
 ionic_lif_free(struct ionic_lif *lif)
 {
+       if (lif->adminqcq) {
+               ionic_qcq_free(lif->adminqcq);
+               lif->adminqcq = NULL;
+       }
+
        if (lif->info) {
                rte_memzone_free(lif->info_z);
                lif->info = NULL;
        }
 }
 
+static void
+ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq)
+{
+       struct ionic_dev *idev = &lif->adapter->idev;
+
+       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);
+
+       qcq->flags &= ~IONIC_QCQ_F_INITED;
+}
+
+bool
+ionic_adminq_service(struct ionic_cq *cq, uint32_t cq_desc_index,
+               void *cb_arg __rte_unused)
+{
+       struct ionic_admin_comp *cq_desc_base = cq->base;
+       struct ionic_admin_comp *cq_desc = &cq_desc_base[cq_desc_index];
+
+       if (!color_match(cq_desc->color, cq->done_color))
+               return false;
+
+       ionic_q_service(cq->bound_q, cq_desc_index, cq_desc->comp_index, NULL);
+
+       return true;
+}
+
+/* This acts like ionic_napi */
+int
+ionic_qcq_service(struct ionic_qcq *qcq, int budget, ionic_cq_cb cb,
+               void *cb_arg)
+{
+       struct ionic_cq *cq = &qcq->cq;
+       uint32_t work_done;
+
+       work_done = ionic_cq_service(cq, budget, cb, cb_arg);
+
+       return work_done;
+}
+
+static int
+ionic_lif_adminq_init(struct ionic_lif *lif)
+{
+       struct ionic_dev *idev = &lif->adapter->idev;
+       struct ionic_qcq *qcq = lif->adminqcq;
+       struct ionic_queue *q = &qcq->q;
+       struct ionic_q_init_comp comp;
+       int err;
+
+       ionic_dev_cmd_adminq_init(idev, qcq, lif->index, qcq->intr.index);
+       err = ionic_dev_cmd_wait_check(idev, IONIC_DEVCMD_TIMEOUT);
+       if (err)
+               return err;
+
+       ionic_dev_cmd_comp(idev, &comp);
+
+       q->hw_type = comp.hw_type;
+       q->hw_index = comp.hw_index;
+       q->db = ionic_db_map(lif, q);
+
+       IONIC_PRINT(DEBUG, "adminq->hw_type %d", q->hw_type);
+       IONIC_PRINT(DEBUG, "adminq->hw_index %d", q->hw_index);
+       IONIC_PRINT(DEBUG, "adminq->db %p", q->db);
+
+       if (qcq->flags & IONIC_QCQ_F_INTR)
+               ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+                       IONIC_INTR_MASK_CLEAR);
+
+       qcq->flags |= IONIC_QCQ_F_INITED;
+
+       return 0;
+}
+
 int
 ionic_lif_init(struct ionic_lif *lif)
 {
@@ -82,6 +427,10 @@ ionic_lif_init(struct ionic_lif *lif)
 
        lif->hw_index = comp.hw_index;
 
+       err = ionic_lif_adminq_init(lif);
+       if (err)
+               return err;
+
        lif->state |= IONIC_LIF_F_INITED;
 
        return 0;
@@ -93,6 +442,8 @@ ionic_lif_deinit(struct ionic_lif *lif)
        if (!(lif->state & IONIC_LIF_F_INITED))
                return;
 
+       ionic_lif_qcq_deinit(lif, lif->adminqcq);
+
        lif->state &= ~IONIC_LIF_F_INITED;
 }
 
index 6e3233d..96522c5 100644 (file)
 #include "ionic_osdep.h"
 #include "ionic_dev.h"
 
+#define IONIC_ADMINQ_LENGTH    16      /* must be a power of two */
+
+#define IONIC_QCQ_F_INITED     BIT(0)
+#define IONIC_QCQ_F_SG         BIT(1)
+#define IONIC_QCQ_F_INTR       BIT(2)
+
+/* Queue / Completion Queue */
+struct ionic_qcq {
+       struct ionic_queue q;        /**< Queue */
+       struct ionic_cq cq;          /**< Completion Queue */
+       struct ionic_lif *lif;       /**< LIF */
+       struct rte_mempool *mb_pool; /**< mbuf pool to populate the RX ring */
+       const struct rte_memzone *base_z;
+       void *base;
+       rte_iova_t base_pa;
+       uint32_t total_size;
+       uint32_t flags;
+       struct ionic_intr_info intr;
+};
+
 #define IONIC_LIF_F_INITED             BIT(0)
 
 #define IONIC_LIF_NAME_MAX_SZ          (32)
@@ -25,6 +45,9 @@ struct ionic_lif {
        uint32_t hw_index;
        uint32_t state;
        uint32_t kern_pid;
+       rte_spinlock_t adminq_lock;
+       rte_spinlock_t adminq_service_lock;
+       struct ionic_qcq *adminqcq;
        struct ionic_doorbell __iomem *kern_dbpage;
        char name[IONIC_LIF_NAME_MAX_SZ];
        uint32_t info_sz;
@@ -47,4 +70,17 @@ int ionic_lif_start(struct ionic_lif *lif);
 int ionic_lif_configure(struct ionic_lif *lif);
 void ionic_lif_reset(struct ionic_lif *lif);
 
+int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr);
+void ionic_intr_free(struct ionic_lif *lif, struct ionic_intr_info *intr);
+
+bool ionic_adminq_service(struct ionic_cq *cq, uint32_t cq_desc_index,
+       void *cb_arg);
+int ionic_qcq_service(struct ionic_qcq *qcq, int budget, ionic_cq_cb cb,
+       void *cb_arg);
+
+void ionic_qcq_free(struct ionic_qcq *qcq);
+
+int ionic_qcq_enable(struct ionic_qcq *qcq);
+int ionic_qcq_disable(struct ionic_qcq *qcq);
+
 #endif /* _IONIC_LIF_H_ */
index 0b420ee..b828d23 100644 (file)
@@ -5,6 +5,190 @@
 #include <rte_memzone.h>
 
 #include "ionic.h"
+#include "ionic_ethdev.h"
+#include "ionic_lif.h"
+
+static const char *
+ionic_error_to_str(enum ionic_status_code code)
+{
+       switch (code) {
+       case IONIC_RC_SUCCESS:
+               return "IONIC_RC_SUCCESS";
+       case IONIC_RC_EVERSION:
+               return "IONIC_RC_EVERSION";
+       case IONIC_RC_EOPCODE:
+               return "IONIC_RC_EOPCODE";
+       case IONIC_RC_EIO:
+               return "IONIC_RC_EIO";
+       case IONIC_RC_EPERM:
+               return "IONIC_RC_EPERM";
+       case IONIC_RC_EQID:
+               return "IONIC_RC_EQID";
+       case IONIC_RC_EQTYPE:
+               return "IONIC_RC_EQTYPE";
+       case IONIC_RC_ENOENT:
+               return "IONIC_RC_ENOENT";
+       case IONIC_RC_EINTR:
+               return "IONIC_RC_EINTR";
+       case IONIC_RC_EAGAIN:
+               return "IONIC_RC_EAGAIN";
+       case IONIC_RC_ENOMEM:
+               return "IONIC_RC_ENOMEM";
+       case IONIC_RC_EFAULT:
+               return "IONIC_RC_EFAULT";
+       case IONIC_RC_EBUSY:
+               return "IONIC_RC_EBUSY";
+       case IONIC_RC_EEXIST:
+               return "IONIC_RC_EEXIST";
+       case IONIC_RC_EINVAL:
+               return "IONIC_RC_EINVAL";
+       case IONIC_RC_ENOSPC:
+               return "IONIC_RC_ENOSPC";
+       case IONIC_RC_ERANGE:
+               return "IONIC_RC_ERANGE";
+       case IONIC_RC_BAD_ADDR:
+               return "IONIC_RC_BAD_ADDR";
+       case IONIC_RC_DEV_CMD:
+               return "IONIC_RC_DEV_CMD";
+       case IONIC_RC_ERROR:
+               return "IONIC_RC_ERROR";
+       case IONIC_RC_ERDMA:
+               return "IONIC_RC_ERDMA";
+       default:
+               return "IONIC_RC_UNKNOWN";
+       }
+}
+
+static const char *
+ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
+{
+       switch (opcode) {
+       case IONIC_CMD_NOP:
+               return "IONIC_CMD_NOP";
+       case IONIC_CMD_INIT:
+               return "IONIC_CMD_INIT";
+       case IONIC_CMD_RESET:
+               return "IONIC_CMD_RESET";
+       case IONIC_CMD_IDENTIFY:
+               return "IONIC_CMD_IDENTIFY";
+       case IONIC_CMD_GETATTR:
+               return "IONIC_CMD_GETATTR";
+       case IONIC_CMD_SETATTR:
+               return "IONIC_CMD_SETATTR";
+       case IONIC_CMD_PORT_IDENTIFY:
+               return "IONIC_CMD_PORT_IDENTIFY";
+       case IONIC_CMD_PORT_INIT:
+               return "IONIC_CMD_PORT_INIT";
+       case IONIC_CMD_PORT_RESET:
+               return "IONIC_CMD_PORT_RESET";
+       case IONIC_CMD_PORT_GETATTR:
+               return "IONIC_CMD_PORT_GETATTR";
+       case IONIC_CMD_PORT_SETATTR:
+               return "IONIC_CMD_PORT_SETATTR";
+       case IONIC_CMD_LIF_INIT:
+               return "IONIC_CMD_LIF_INIT";
+       case IONIC_CMD_LIF_RESET:
+               return "IONIC_CMD_LIF_RESET";
+       case IONIC_CMD_LIF_IDENTIFY:
+               return "IONIC_CMD_LIF_IDENTIFY";
+       case IONIC_CMD_LIF_SETATTR:
+               return "IONIC_CMD_LIF_SETATTR";
+       case IONIC_CMD_LIF_GETATTR:
+               return "IONIC_CMD_LIF_GETATTR";
+       case IONIC_CMD_RX_MODE_SET:
+               return "IONIC_CMD_RX_MODE_SET";
+       case IONIC_CMD_RX_FILTER_ADD:
+               return "IONIC_CMD_RX_FILTER_ADD";
+       case IONIC_CMD_RX_FILTER_DEL:
+               return "IONIC_CMD_RX_FILTER_DEL";
+       case IONIC_CMD_Q_INIT:
+               return "IONIC_CMD_Q_INIT";
+       case IONIC_CMD_Q_CONTROL:
+               return "IONIC_CMD_Q_CONTROL";
+       case IONIC_CMD_RDMA_RESET_LIF:
+               return "IONIC_CMD_RDMA_RESET_LIF";
+       case IONIC_CMD_RDMA_CREATE_EQ:
+               return "IONIC_CMD_RDMA_CREATE_EQ";
+       case IONIC_CMD_RDMA_CREATE_CQ:
+               return "IONIC_CMD_RDMA_CREATE_CQ";
+       case IONIC_CMD_RDMA_CREATE_ADMINQ:
+               return "IONIC_CMD_RDMA_CREATE_ADMINQ";
+       default:
+               return "DEVCMD_UNKNOWN";
+       }
+}
+
+int
+ionic_adminq_check_err(struct ionic_admin_ctx *ctx, bool timeout)
+{
+       const char *name;
+       const char *status;
+
+       if (ctx->comp.comp.status || timeout) {
+               name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
+               status = ionic_error_to_str(ctx->comp.comp.status);
+               IONIC_PRINT(ERR, "%s (%d) failed: %s (%d)",
+                       name,
+                       ctx->cmd.cmd.opcode,
+                       timeout ? "TIMEOUT" : status,
+                       timeout ? -1 : ctx->comp.comp.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+ionic_wait_ctx_for_completion(struct ionic_lif *lif, struct ionic_qcq *qcq,
+               struct ionic_admin_ctx *ctx, unsigned long max_wait)
+{
+       unsigned long step_msec = 1;
+       unsigned int max_wait_msec = max_wait * 1000;
+       unsigned long elapsed_msec = 0;
+       int budget = 8;
+
+       while (ctx->pending_work && elapsed_msec < max_wait_msec) {
+               /*
+                * Locking here as adminq is served inline (this could be called
+                * from multiple places)
+                */
+               rte_spinlock_lock(&lif->adminq_service_lock);
+
+               ionic_qcq_service(qcq, budget, ionic_adminq_service, NULL);
+
+               rte_spinlock_unlock(&lif->adminq_service_lock);
+
+               msec_delay(step_msec);
+               elapsed_msec += step_msec;
+       }
+
+       return (!ctx->pending_work);
+}
+
+int
+ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
+{
+       struct ionic_qcq *qcq = lif->adminqcq;
+       bool done;
+       int err;
+
+       IONIC_PRINT(DEBUG, "Sending %s to the admin queue",
+               ionic_opcode_to_str(ctx->cmd.cmd.opcode));
+
+       err = ionic_adminq_post(lif, ctx);
+       if (err) {
+               IONIC_PRINT(ERR, "Failure posting to the admin queue %d (%d)",
+                       ctx->cmd.cmd.opcode, err);
+
+               return err;
+       }
+
+       done = ionic_wait_ctx_for_completion(lif, qcq, ctx,
+               IONIC_DEVCMD_TIMEOUT);
+
+       err = ionic_adminq_check_err(ctx, !done /* timed out */);
+       return err;
+}
 
 static int
 ionic_dev_cmd_wait(struct ionic_dev *idev, unsigned long max_wait)