+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;
+}
+