be2net: Add MCC queue mechanism for BE cmds

Currenlty all cmds use the blocking MCC mbox to post cmds. An mbox cmd is protected
via a spin_lock(cmd_lock) and not spin_lock_bh() as it is undesirable
to disable BHs while a blocking mbox cmd is in progress (and take long to finish.)
This can lockup a cmd in progress in process context. Instead cmds that may be
called in BH context must use the MCC queue to post cmds. The cmd completions
are rcvd in a separate completion queue and the events are placed in the tx-event
queue.

Signed-off-by: Sathya Perla <sathyap@serverengines.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c
index d444aed..f1ec191 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/benet/be_cmds.c
@@ -17,6 +17,90 @@
 
 #include "be.h"
 
+void be_mcc_notify(struct be_ctrl_info *ctrl)
+{
+	struct be_queue_info *mccq = &ctrl->mcc_obj.q;
+	u32 val = 0;
+
+	val |= mccq->id & DB_MCCQ_RING_ID_MASK;
+	val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
+	iowrite32(val, ctrl->db + DB_MCCQ_OFFSET);
+}
+
+/* To check if valid bit is set, check the entire word as we don't know
+ * the endianness of the data (old entry is host endian while a new entry is
+ * little endian) */
+static inline bool be_mcc_compl_is_new(struct be_mcc_cq_entry *compl)
+{
+	if (compl->flags != 0) {
+		compl->flags = le32_to_cpu(compl->flags);
+		BUG_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0);
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/* Need to reset the entire word that houses the valid bit */
+static inline void be_mcc_compl_use(struct be_mcc_cq_entry *compl)
+{
+	compl->flags = 0;
+}
+
+static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
+	struct be_mcc_cq_entry *compl)
+{
+	u16 compl_status, extd_status;
+
+	/* Just swap the status to host endian; mcc tag is opaquely copied
+	 * from mcc_wrb */
+	be_dws_le_to_cpu(compl, 4);
+
+	compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
+				CQE_STATUS_COMPL_MASK;
+	if (compl_status != MCC_STATUS_SUCCESS) {
+		extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
+				CQE_STATUS_EXTD_MASK;
+		printk(KERN_WARNING DRV_NAME
+			" error in cmd completion: status(compl/extd)=%d/%d\n",
+			compl_status, extd_status);
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct be_mcc_cq_entry *be_mcc_compl_get(struct be_ctrl_info *ctrl)
+{
+	struct be_queue_info *mcc_cq = &ctrl->mcc_obj.cq;
+	struct be_mcc_cq_entry *compl = queue_tail_node(mcc_cq);
+
+	if (be_mcc_compl_is_new(compl)) {
+		queue_tail_inc(mcc_cq);
+		return compl;
+	}
+	return NULL;
+}
+
+void be_process_mcc(struct be_ctrl_info *ctrl)
+{
+	struct be_mcc_cq_entry *compl;
+	int num = 0;
+
+	spin_lock_bh(&ctrl->mcc_cq_lock);
+	while ((compl = be_mcc_compl_get(ctrl))) {
+		if (!(compl->flags & CQE_FLAGS_ASYNC_MASK)) {
+			be_mcc_compl_process(ctrl, compl);
+			atomic_dec(&ctrl->mcc_obj.q.used);
+		}
+		be_mcc_compl_use(compl);
+		num++;
+	}
+	if (num)
+		be_cq_notify(ctrl, ctrl->mcc_obj.cq.id, true, num);
+	spin_unlock_bh(&ctrl->mcc_cq_lock);
+}
+
 static int be_mbox_db_ready_wait(void __iomem *db)
 {
 	int cnt = 0, wait = 5;
@@ -44,11 +128,11 @@
 
 /*
  * Insert the mailbox address into the doorbell in two steps
+ * Polls on the mbox doorbell till a command completion (or a timeout) occurs
  */
 static int be_mbox_db_ring(struct be_ctrl_info *ctrl)
 {
 	int status;
-	u16 compl_status, extd_status;
 	u32 val = 0;
 	void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
 	struct be_dma_mem *mbox_mem = &ctrl->mbox_mem;
@@ -79,24 +163,17 @@
 	if (status != 0)
 		return status;
 
-	/* compl entry has been made now */
-	be_dws_le_to_cpu(cqe, sizeof(*cqe));
-	if (!(cqe->flags & CQE_FLAGS_VALID_MASK)) {
-		printk(KERN_WARNING DRV_NAME ": ERROR invalid mbox compl\n");
+	/* A cq entry has been made now */
+	if (be_mcc_compl_is_new(cqe)) {
+		status = be_mcc_compl_process(ctrl, &mbox->cqe);
+		be_mcc_compl_use(cqe);
+		if (status)
+			return status;
+	} else {
+		printk(KERN_WARNING DRV_NAME "invalid mailbox completion\n");
 		return -1;
 	}
-
-	compl_status = (cqe->status >> CQE_STATUS_COMPL_SHIFT) &
-				CQE_STATUS_COMPL_MASK;
-	if (compl_status != MCC_STATUS_SUCCESS) {
-		extd_status = (cqe->status >> CQE_STATUS_EXTD_SHIFT) &
-				CQE_STATUS_EXTD_MASK;
-		printk(KERN_WARNING DRV_NAME
-			": ERROR in cmd compl. status(compl/extd)=%d/%d\n",
-			compl_status, extd_status);
-	}
-
-	return compl_status;
+	return 0;
 }
 
 static int be_POST_stage_get(struct be_ctrl_info *ctrl, u16 *stage)
@@ -235,6 +312,18 @@
 	return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
 }
 
+static inline struct be_mcc_wrb *wrb_from_mcc(struct be_queue_info *mccq)
+{
+	struct be_mcc_wrb *wrb = NULL;
+	if (atomic_read(&mccq->used) < mccq->len) {
+		wrb = queue_head_node(mccq);
+		queue_head_inc(mccq);
+		atomic_inc(&mccq->used);
+		memset(wrb, 0, sizeof(*wrb));
+	}
+	return wrb;
+}
+
 int be_cmd_eq_create(struct be_ctrl_info *ctrl,
 		struct be_queue_info *eq, int eq_delay)
 {
@@ -244,7 +333,7 @@
 	struct be_dma_mem *q_mem = &eq->dma_mem;
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -272,7 +361,7 @@
 		eq->id = le16_to_cpu(resp->eq_id);
 		eq->created = true;
 	}
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -284,7 +373,7 @@
 	struct be_cmd_resp_mac_query *resp = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -304,7 +393,7 @@
 	if (!status)
 		memcpy(mac_addr, resp->mac.addr, ETH_ALEN);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -315,7 +404,7 @@
 	struct be_cmd_req_pmac_add *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -332,7 +421,7 @@
 		*pmac_id = le32_to_cpu(resp->pmac_id);
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -342,7 +431,7 @@
 	struct be_cmd_req_pmac_del *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -354,7 +443,7 @@
 	req->pmac_id = cpu_to_le32(pmac_id);
 
 	status = be_mbox_db_ring(ctrl);
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -370,7 +459,7 @@
 	void *ctxt = &req->context;
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -388,7 +477,7 @@
 	AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
 	AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
 	AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
-	AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 0);
+	AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
 	AMAP_SET_BITS(struct amap_cq_context, func, ctxt, ctrl->pci_func);
 	be_dws_cpu_to_le(ctxt, sizeof(req->context));
 
@@ -399,7 +488,56 @@
 		cq->id = le16_to_cpu(resp->cq_id);
 		cq->created = true;
 	}
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
+
+	return status;
+}
+
+static u32 be_encoded_q_len(int q_len)
+{
+	u32 len_encoded = fls(q_len); /* log2(len) + 1 */
+	if (len_encoded == 16)
+		len_encoded = 0;
+	return len_encoded;
+}
+
+int be_cmd_mccq_create(struct be_ctrl_info *ctrl,
+			struct be_queue_info *mccq,
+			struct be_queue_info *cq)
+{
+	struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+	struct be_cmd_req_mcc_create *req = embedded_payload(wrb);
+	struct be_dma_mem *q_mem = &mccq->dma_mem;
+	void *ctxt = &req->context;
+	int status;
+
+	spin_lock(&ctrl->mbox_lock);
+	memset(wrb, 0, sizeof(*wrb));
+
+	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+
+	be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+			OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+
+	req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+
+	AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt, ctrl->pci_func);
+	AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1);
+	AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt,
+		be_encoded_q_len(mccq->len));
+	AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id);
+
+	be_dws_cpu_to_le(ctxt, sizeof(req->context));
+
+	be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+	status = be_mbox_db_ring(ctrl);
+	if (!status) {
+		struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
+		mccq->id = le16_to_cpu(resp->id);
+		mccq->created = true;
+	}
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -415,7 +553,7 @@
 	int status;
 	u32 len_encoded;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -446,7 +584,7 @@
 		txq->id = le16_to_cpu(resp->cid);
 		txq->created = true;
 	}
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -460,7 +598,7 @@
 	struct be_dma_mem *q_mem = &rxq->dma_mem;
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -482,7 +620,7 @@
 		rxq->id = le16_to_cpu(resp->id);
 		rxq->created = true;
 	}
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -496,7 +634,7 @@
 	u8 subsys = 0, opcode = 0;
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 
 	memset(wrb, 0, sizeof(*wrb));
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -518,6 +656,10 @@
 		subsys = CMD_SUBSYSTEM_ETH;
 		opcode = OPCODE_ETH_RX_DESTROY;
 		break;
+	case QTYPE_MCCQ:
+		subsys = CMD_SUBSYSTEM_COMMON;
+		opcode = OPCODE_COMMON_MCC_DESTROY;
+		break;
 	default:
 		printk(KERN_WARNING DRV_NAME ":bad Q type in Q destroy cmd\n");
 		status = -1;
@@ -528,7 +670,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 err:
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -541,7 +683,7 @@
 	struct be_cmd_req_if_create *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -562,7 +704,7 @@
 			*pmac_id = le32_to_cpu(resp->pmac_id);
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -572,7 +714,7 @@
 	struct be_cmd_req_if_destroy *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -583,7 +725,7 @@
 	req->interface_id = cpu_to_le32(interface_id);
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 
 	return status;
 }
@@ -598,7 +740,7 @@
 	struct be_sge *sge = nonembedded_sgl(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	memset(req, 0, sizeof(*req));
@@ -617,7 +759,7 @@
 		be_dws_le_to_cpu(&resp->hw_stats, sizeof(resp->hw_stats));
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -628,7 +770,7 @@
 	struct be_cmd_req_link_status *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -646,7 +788,7 @@
 		link->speed = PHY_LINK_SPEED_ZERO;
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -656,7 +798,7 @@
 	struct be_cmd_req_get_fw_version *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -670,7 +812,7 @@
 		strncpy(fw_ver, resp->firmware_version_string, FW_VER_LEN);
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -681,7 +823,7 @@
 	struct be_cmd_req_modify_eq_delay *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -696,7 +838,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -707,7 +849,7 @@
 	struct be_cmd_req_vlan_config *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -726,7 +868,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -736,7 +878,7 @@
 	struct be_cmd_req_promiscuous_config *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -751,7 +893,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -762,7 +904,7 @@
 	struct be_cmd_req_mcast_mac_config *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 	memset(wrb, 0, sizeof(*wrb));
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -780,7 +922,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -790,7 +932,7 @@
 	struct be_cmd_req_set_flow_control *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 
 	memset(wrb, 0, sizeof(*wrb));
 
@@ -804,7 +946,7 @@
 
 	status = be_mbox_db_ring(ctrl);
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -814,7 +956,7 @@
 	struct be_cmd_req_get_flow_control *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 
 	memset(wrb, 0, sizeof(*wrb));
 
@@ -831,7 +973,7 @@
 		*rx_fc = le16_to_cpu(resp->rx_flow_control);
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }
 
@@ -841,7 +983,7 @@
 	struct be_cmd_req_query_fw_cfg *req = embedded_payload(wrb);
 	int status;
 
-	spin_lock(&ctrl->cmd_lock);
+	spin_lock(&ctrl->mbox_lock);
 
 	memset(wrb, 0, sizeof(*wrb));
 
@@ -856,6 +998,6 @@
 		*port_num = le32_to_cpu(resp->phys_port);
 	}
 
-	spin_unlock(&ctrl->cmd_lock);
+	spin_unlock(&ctrl->mbox_lock);
 	return status;
 }