diff --git a/drivers/net/ionic/ionic.h b/drivers/net/ionic/ionic.h
index a30e1cf20..3a9bb7b22 100644
--- a/drivers/net/ionic/ionic.h
+++ b/drivers/net/ionic/ionic.h
@@ -55,11 +55,15 @@ 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_lif *lif, 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);
 
diff --git a/drivers/net/ionic/ionic_dev.c b/drivers/net/ionic/ionic_dev.c
index d0a4d5268..fc165d0a8 100644
--- a/drivers/net/ionic/ionic_dev.c
+++ b/drivers/net/ionic/ionic_dev.c
@@ -293,6 +293,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)
 {
@@ -306,3 +312,253 @@ 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 = ilog2(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_init_print(ERR, "Descriptor size is %lu", 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_init_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, const char *base, 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 = ilog2(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;
+}
diff --git a/drivers/net/ionic/ionic_dev.h b/drivers/net/ionic/ionic_dev.h
index 812d800d9..ada07edff 100644
--- a/drivers/net/ionic/ionic_dev.h
+++ b/drivers/net/ionic/ionic_dev.h
@@ -20,6 +20,9 @@
  */
 #define IONIC_API_VERSION		"3"
 
+#define IONIC_MAX_RING_DESC		32768
+#define IONIC_MIN_RING_DESC		16
+
 #define IONIC_LIFS_MAX			1024
 
 #define IONIC_DEVCMD_TIMEOUT	30 /* devcmd_timeout */
@@ -132,6 +135,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)
 
@@ -140,11 +181,34 @@ struct ionic_intr_info {
 	int index;
 	uint32_t vector;
 	struct ionic_intr __iomem *ctrl;
-	uint64_t rearm_count;
+};
+
+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);
@@ -177,7 +241,37 @@ 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, const char *base, 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_ */
diff --git a/drivers/net/ionic/ionic_lif.c b/drivers/net/ionic/ionic_lif.c
index c36bb6bc3..f2ede6d23 100644
--- a/drivers/net/ionic/ionic_lif.c
+++ b/drivers/net/ionic/ionic_lif.c
@@ -10,6 +10,259 @@
 #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_init_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_init_print(ERR, "Cannot allocate queue info");
+		return -ENOMEM;
+	}
+
+	new->q.type = type;
+
+	err = ionic_q_init(lif, idev, &new->q, index, base, num_descs,
+			desc_size, sg_desc_size, pid);
+
+	if (err) {
+		ionic_init_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_init_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_init_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_init_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,10 +280,12 @@ 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_init_print(DEBUG, "Allocating Lif Info");
+	rte_spinlock_init(&lif->adminq_lock);
+	rte_spinlock_init(&lif->adminq_service_lock);
 
 	lif->kern_pid = 0;
 
@@ -43,6 +298,17 @@ ionic_lif_alloc(struct ionic_lif *lif)
 		return -ENOMEM;
 	}
 
+	ionic_init_print(DEBUG, "Allocating Admin Queue");
+
+	err = ionic_admin_qcq_alloc(lif);
+
+	if (err) {
+		ionic_init_print(ERR, "Cannot allocate admin queue");
+		return err;
+	}
+
+	ionic_init_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,
@@ -63,10 +329,91 @@ 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);
 }
 
+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_init_print(DEBUG, "adminq->hw_type %d\n", q->hw_type);
+	ionic_init_print(DEBUG, "adminq->hw_index %d\n", q->hw_index);
+	ionic_init_print(DEBUG, "adminq->db %p\n", 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 +429,11 @@ 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 +445,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;
 }
 
diff --git a/drivers/net/ionic/ionic_lif.h b/drivers/net/ionic/ionic_lif.h
index 6b912efa0..cedd8a721 100644
--- a/drivers/net/ionic/ionic_lif.h
+++ b/drivers/net/ionic/ionic_lif.h
@@ -13,6 +13,26 @@
 #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;
@@ -48,4 +71,17 @@ int ionic_lif_stop(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_ */
diff --git a/drivers/net/ionic/ionic_main.c b/drivers/net/ionic/ionic_main.c
index 0feb713a9..45a6dc26c 100644
--- a/drivers/net/ionic/ionic_main.c
+++ b/drivers/net/ionic/ionic_main.c
@@ -5,6 +5,194 @@
 #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_lif *lif, 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_init_print(ERR, "%s (%d) failed: %s (%d)\n",
+				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_init_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_init_print(ERR, "Failure posting to the admin queue %d "
+				"(%d)\n",
+				ctx->cmd.cmd.opcode, err);
+
+		return err;
+	}
+
+	done = ionic_wait_ctx_for_completion(lif, qcq, ctx,
+			IONIC_DEVCMD_TIMEOUT);
+
+	err = ionic_adminq_check_err(lif, ctx, !done /* timed out */);
+
+	return err;
+}
 
 static int
 ionic_dev_cmd_wait(struct ionic_dev *idev, unsigned long max_wait)
