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);
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)
{
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;
+}
#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 */
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)
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);
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_ */
#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)
{
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);
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,
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)
{
lif->hw_index = comp.hw_index;
+ err = ionic_lif_adminq_init(lif);
+ if (err)
+ return err;
+
lif->state |= IONIC_LIF_F_INITED;
return 0;
if (!(lif->state & IONIC_LIF_F_INITED))
return;
+ ionic_lif_qcq_deinit(lif, lif->adminqcq);
+
lif->state &= ~IONIC_LIF_F_INITED;
}
#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)
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;
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_ */
#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)