Browse Source

qcacmn: Add changes for per NAPI or per Rx CE LRO manager

Make changes for per per Rx context LRO manager, this addresses
all parallel Rx concurrency issues. There by removes all the contention.

Change-Id: I90604ffdf7b7fd930eee636426a4c7fc9b92c7d7
CRs-Fixed: 1079320
Manjunathappa Prakash 8 years ago
parent
commit
2146da30dd

+ 3 - 2
dp/inc/cdp_txrx_lro.h

@@ -32,7 +32,8 @@
 #ifndef _CDP_TXRX_LRO_H_
 #define _CDP_TXRX_LRO_H_
 
-void ol_register_lro_flush_cb(void (handler)(void *), void *data);
-void ol_deregister_lro_flush_cb(void);
+void ol_register_lro_flush_cb(void (lro_flush_cb)(void *),
+				void *(lro_init_cb)(void));
+void ol_deregister_lro_flush_cb(void (lro_deinit_cb)(void *));
 
 #endif /* _CDP_TXRX_LRO_H_ */

+ 9 - 2
hif/inc/hif.h

@@ -182,6 +182,10 @@ struct qca_napi_info {
 	uint8_t              id;
 	int                  irq;
 	struct qca_napi_stat stats[NR_CPUS];
+	/* will only be present for data rx CE's */
+	void (*lro_flush_cb)(void *);
+	void                 *lro_ctx;
+	qdf_spinlock_t lro_unloading_lock;
 };
 
 /**
@@ -696,8 +700,10 @@ void hif_crash_shutdown(struct hif_opaque_softc *hif_ctx);
 void hif_get_hw_info(struct hif_opaque_softc *scn, u32 *version, u32 *revision,
 		     const char **target_name);
 void hif_lro_flush_cb_register(struct hif_opaque_softc *scn,
-			       void (handler)(void *), void *data);
-void hif_lro_flush_cb_deregister(struct hif_opaque_softc *scn);
+			       void (lro_flush_handler)(void *),
+			       void *(lro_init_handler)(void));
+void hif_lro_flush_cb_deregister(struct hif_opaque_softc *scn,
+				 void (lro_deinit_cb)(void *));
 bool hif_needs_bmi(struct hif_opaque_softc *scn);
 enum qdf_bus_type hif_get_bus_type(struct hif_opaque_softc *hif_hdl);
 struct hif_target_info *hif_get_target_info_handle(struct hif_opaque_softc *
@@ -734,6 +740,7 @@ int hif_bus_reset_resume(struct hif_opaque_softc *scn);
 
 void hif_set_attribute(struct hif_opaque_softc *osc, uint8_t hif_attrib);
 
+void *hif_get_lro_info(int ctx_id, struct hif_opaque_softc *hif_hdl);
 #ifdef WLAN_SUSPEND_RESUME_TEST
 typedef void (*hif_fake_resume_callback)(uint32_t val);
 void hif_fake_apps_suspend(struct hif_opaque_softc *hif_ctx,

+ 8 - 0
hif/inc/hif_napi.h

@@ -77,13 +77,21 @@ enum qca_napi_event {
 	NAPI_EVT_USR_NORMAL
 };
 
+
 /**
  * Macros to map ids -returned by ...create()- to pipes and vice versa
  */
 #define NAPI_ID2PIPE(i) ((i)-1)
 #define NAPI_PIPE2ID(p) ((p)+1)
 
+int hif_napi_lro_flush_cb_register(struct hif_opaque_softc *hif_hdl,
+				   void (lro_flush_handler)(void *),
+				   void *(lro_init_handler)(void));
+
+void hif_napi_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl,
+				      void (lro_deinit_cb)(void *));
 
+void *hif_napi_get_lro_info(struct hif_opaque_softc *hif_hdl, int napi_id);
 #ifdef FEATURE_NAPI
 
 /**

+ 5 - 2
hif/src/ce/ce_api.h

@@ -479,10 +479,13 @@ static inline void ce_pkt_error_count_incr(
 }
 
 bool ce_check_rx_pending(struct CE_state *CE_state);
+void *hif_ce_get_lro_ctx(struct hif_opaque_softc *hif_hdl, int ctx_id);
 #if defined(FEATURE_LRO)
 int ce_lro_flush_cb_register(struct hif_opaque_softc *scn,
-			     void (handler)(void *), void *data);
-int ce_lro_flush_cb_deregister(struct hif_opaque_softc *scn);
+			     void (handler)(void *),
+			     void *(lro_init_handler)(void));
+int ce_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl,
+			       void (lro_deinit_cb)(void *));
 #endif
 struct ce_ops *ce_services_srng(void);
 struct ce_ops *ce_services_legacy(void);

+ 1 - 0
hif/src/ce/ce_internal.h

@@ -150,6 +150,7 @@ struct CE_state {
 	bool htt_rx_data;
 	void (*lro_flush_cb)(void *);
 	void *lro_data;
+	qdf_spinlock_t lro_unloading_lock;
 };
 
 /* Descriptor rings must be aligned to this boundary */

+ 31 - 2
hif/src/ce/ce_main.c

@@ -756,6 +756,7 @@ struct CE_handle *ce_init(struct hif_softc *scn,
 		CE_state->ctrl_addr = ctrl_addr;
 		CE_state->state = CE_RUNNING;
 		CE_state->attr_flags = attr->flags;
+		qdf_spinlock_create(&CE_state->lro_unloading_lock);
 	}
 	CE_state->scn = scn;
 
@@ -2497,6 +2498,18 @@ u32 shadow_dst_wr_ind_addr(struct hif_softc *scn, u32 ctrl_addr)
 #endif
 
 #if defined(FEATURE_LRO)
+void *hif_ce_get_lro_ctx(struct hif_opaque_softc *hif_hdl, int ctx_id)
+{
+	struct CE_state *ce_state;
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
+
+	QDF_ASSERT(scn != NULL);
+
+	ce_state = scn->ce_id_to_state[ctx_id];
+
+	return ce_state->lro_data;
+}
+
 /**
  * ce_lro_flush_cb_register() - register the LRO flush
  * callback
@@ -2509,12 +2522,14 @@ u32 shadow_dst_wr_ind_addr(struct hif_softc *scn, u32 ctrl_addr)
  * Return: Number of instances the callback is registered for
  */
 int ce_lro_flush_cb_register(struct hif_opaque_softc *hif_hdl,
-			     void (handler)(void *), void *data)
+			     void (handler)(void *),
+			     void *(lro_init_handler)(void))
 {
 	int rc = 0;
 	int i;
 	struct CE_state *ce_state;
 	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
+	void *data = NULL;
 
 	QDF_ASSERT(scn != NULL);
 
@@ -2522,6 +2537,12 @@ int ce_lro_flush_cb_register(struct hif_opaque_softc *hif_hdl,
 		for (i = 0; i < scn->ce_count; i++) {
 			ce_state = scn->ce_id_to_state[i];
 			if ((ce_state != NULL) && (ce_state->htt_rx_data)) {
+				data = lro_init_handler();
+				if (data == NULL) {
+					HIF_ERROR("%s: Failed to init LRO for CE %d",
+						  __func__, i);
+					continue;
+				}
 				ce_state->lro_flush_cb = handler;
 				ce_state->lro_data = data;
 				rc++;
@@ -2542,7 +2563,8 @@ int ce_lro_flush_cb_register(struct hif_opaque_softc *hif_hdl,
  *
  * Return: Number of instances the callback is de-registered
  */
-int ce_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl)
+int ce_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl,
+			       void (lro_deinit_cb)(void *))
 {
 	int rc = 0;
 	int i;
@@ -2554,8 +2576,15 @@ int ce_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl)
 		for (i = 0; i < scn->ce_count; i++) {
 			ce_state = scn->ce_id_to_state[i];
 			if ((ce_state != NULL) && (ce_state->htt_rx_data)) {
+				qdf_spin_lock_bh(
+					&ce_state->lro_unloading_lock);
 				ce_state->lro_flush_cb = NULL;
+				lro_deinit_cb(ce_state->lro_data);
 				ce_state->lro_data = NULL;
+				qdf_spin_unlock_bh(
+					&ce_state->lro_unloading_lock);
+				qdf_spinlock_destroy(
+					&ce_state->lro_unloading_lock);
 				rc++;
 			}
 		}

+ 4 - 1
hif/src/ce/ce_service.c

@@ -1692,7 +1692,7 @@ static void ce_fastpath_rx_handle(struct CE_state *ce_state,
 	uint32_t write_index;
 
 	qdf_spin_unlock(&ce_state->ce_index_lock);
-	(ce_state->fastpath_handler)(ce_state->context, cmpl_msdus, num_cmpls);
+	(ce_state->fastpath_handler)(ce_state->context,	cmpl_msdus, num_cmpls);
 	qdf_spin_lock(&ce_state->ce_index_lock);
 
 	/* Update Destination Ring Write Index */
@@ -1723,6 +1723,7 @@ static void ce_fastpath_rx_handle(struct CE_state *ce_state,
 static void ce_per_engine_service_fast(struct hif_softc *scn, int ce_id)
 {
 	struct CE_state *ce_state = scn->ce_id_to_state[ce_id];
+	struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn);
 	struct CE_ring_state *dest_ring = ce_state->dest_ring;
 	struct CE_dest_desc *dest_ring_base =
 		(struct CE_dest_desc *)dest_ring->base_addr_owner_space;
@@ -1795,6 +1796,8 @@ more_data:
 
 		qdf_assert_always(nbuf->data != NULL);
 
+		QDF_NBUF_CB_RX_CTX_ID(nbuf) =
+				hif_get_rx_ctx_id(ce_state->id, hif_hdl);
 		cmpl_msdus[nbuf_cmpl_idx++] = nbuf;
 
 		/*

+ 2 - 0
hif/src/ce/ce_tasklet.c

@@ -172,11 +172,13 @@ static void ce_tasklet(unsigned long data)
 		QDF_BUG(0);
 	}
 
+	qdf_spin_lock_bh(&CE_state->lro_unloading_lock);
 	ce_per_engine_service(scn, tasklet_entry->ce_id);
 
 	if (CE_state->lro_flush_cb != NULL) {
 		CE_state->lro_flush_cb(CE_state->lro_data);
 	}
+	qdf_spin_unlock_bh(&CE_state->lro_unloading_lock);
 
 	if (ce_check_rx_pending(CE_state)) {
 		/*

+ 56 - 5
hif/src/hif_main.c

@@ -49,6 +49,7 @@
 #ifdef QCA_WIFI_QCA8074
 #include "hal_api.h"
 #endif
+#include "hif_napi.h"
 
 void hif_dump(struct hif_opaque_softc *hif_ctx, uint8_t cmd_id, bool start)
 {
@@ -812,20 +813,70 @@ struct hif_target_info *hif_get_target_info_handle(
  * Return: void
  */
 void hif_lro_flush_cb_register(struct hif_opaque_softc *scn,
-			       void (handler)(void *), void *data)
+			       void (lro_flush_handler)(void *),
+			       void *(lro_init_handler)(void))
 {
-	ce_lro_flush_cb_register(scn, handler, data);
+	if (hif_napi_enabled(scn, -1))
+		hif_napi_lro_flush_cb_register(scn, lro_flush_handler,
+					       lro_init_handler);
+	else
+		ce_lro_flush_cb_register(scn, lro_flush_handler,
+					lro_init_handler);
+}
+
+/**
+ * hif_get_lro_info - Returns LRO instance for instance ID
+ * @ctx_id: LRO instance ID
+ * @hif_hdl: HIF Context
+ *
+ * Return: Pointer to LRO instance.
+ */
+void *hif_get_lro_info(int ctx_id, struct hif_opaque_softc *hif_hdl)
+{
+	void *data;
+
+	if (hif_napi_enabled(hif_hdl, -1))
+		data = hif_napi_get_lro_info(hif_hdl, ctx_id);
+	else
+		data = hif_ce_get_lro_ctx(hif_hdl, ctx_id);
+
+	return data;
+}
+
+/**
+ * hif_get_rx_ctx_id - Returns LRO instance ID based on underlying LRO instance
+ * @ctx_id: LRO context ID
+ * @hif_hdl: HIF Context
+ *
+ * Return: LRO instance ID
+ */
+int hif_get_rx_ctx_id(int ctx_id, struct hif_opaque_softc *hif_hdl)
+{
+	if (hif_napi_enabled(hif_hdl, -1))
+		return NAPI_PIPE2ID(ctx_id);
+	else
+		return ctx_id;
 }
 
 /**
  * hif_lro_flush_cb_deregister - API to deregister for LRO Flush Callbacks
- * @scn: HIF Context
+ * @hif_hdl: HIF Context
+ * @lro_deinit_cb: LRO deinit callback
  *
  * Return: void
  */
-void hif_lro_flush_cb_deregister(struct hif_opaque_softc *scn)
+void hif_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl,
+				 void (lro_deinit_cb)(void *))
 {
-	ce_lro_flush_cb_deregister(scn);
+	if (hif_napi_enabled(hif_hdl, -1))
+		hif_napi_lro_flush_cb_deregister(hif_hdl, lro_deinit_cb);
+	else
+		ce_lro_flush_cb_deregister(hif_hdl, lro_deinit_cb);
+}
+#else /* !defined(FEATURE_LRO) */
+int hif_get_rx_ctx_id(int ctx_id, struct hif_opaque_softc *hif_hdl)
+{
+	return 0;
 }
 #endif
 

+ 1 - 0
hif/src/hif_main.h

@@ -238,6 +238,7 @@ void hif_wlan_disable(struct hif_softc *scn);
 int hif_target_sleep_state_adjust(struct hif_softc *scn,
 					 bool sleep_ok,
 					 bool wait_for_it);
+int hif_get_rx_ctx_id(int ctx_id, struct hif_opaque_softc *hif_hdl);
 #ifdef HIF_USB
 void hif_usb_get_hw_info(struct hif_softc *scn);
 void hif_ramdump_handler(struct hif_opaque_softc *scn);

+ 127 - 8
hif/src/hif_napi.c

@@ -155,6 +155,7 @@ int hif_napi_create(struct hif_opaque_softc   *hif_ctx,
 			HIF_WARN("%s: bad IRQ value for CE %d: %d",
 				 __func__, i, napii->irq);
 
+		qdf_spinlock_create(&napii->lro_unloading_lock);
 		init_dummy_netdev(&(napii->netdev));
 
 		NAPI_DEBUG("adding napi=%p to netdev=%p (poll=%p, bdgt=%d)",
@@ -274,6 +275,119 @@ int hif_napi_destroy(struct hif_opaque_softc *hif_ctx,
 	return rc;
 }
 
+/**
+ * hif_napi_lro_flush_cb_register() - init and register flush callback for LRO
+ * @hif_hdl: pointer to hif context
+ * @lro_flush_handler: register LRO flush callback
+ * @lro_init_handler: Callback for initializing LRO
+ *
+ * Return: positive value on success and 0 on failure
+ */
+int hif_napi_lro_flush_cb_register(struct hif_opaque_softc *hif_hdl,
+				   void (lro_flush_handler)(void *),
+				   void *(lro_init_handler)(void))
+{
+	int rc = 0;
+	int i;
+	struct CE_state *ce_state;
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
+	void *data = NULL;
+	struct qca_napi_data *napid;
+	struct qca_napi_info *napii;
+
+	QDF_ASSERT(scn != NULL);
+
+	napid = hif_napi_get_all(hif_hdl);
+	if (scn != NULL) {
+		for (i = 0; i < scn->ce_count; i++) {
+			ce_state = scn->ce_id_to_state[i];
+			if ((ce_state != NULL) && (ce_state->htt_rx_data)) {
+				data = lro_init_handler();
+				if (data == NULL) {
+					HIF_ERROR("%s: Failed to init LRO for CE %d",
+						  __func__, i);
+					continue;
+				}
+				napii = &(napid->napis[i]);
+				napii->lro_flush_cb = lro_flush_handler;
+				napii->lro_ctx = data;
+				HIF_ERROR("Registering LRO for ce_id %d NAPI callback for %d flush_cb %p, lro_data %p\n",
+					i, napii->id, napii->lro_flush_cb,
+					napii->lro_ctx);
+				rc++;
+			}
+		}
+	} else {
+		HIF_ERROR("%s: hif_state NULL!", __func__);
+	}
+	return rc;
+}
+
+/**
+ * hif_napi_lro_flush_cb_deregister() - Degregister and free LRO.
+ * @hif: pointer to hif context
+ * @lro_deinit_cb: LRO deinit callback
+ *
+ * Return: NONE
+ */
+void hif_napi_lro_flush_cb_deregister(struct hif_opaque_softc *hif_hdl,
+				     void (lro_deinit_cb)(void *))
+{
+	int i;
+	struct CE_state *ce_state;
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
+	struct qca_napi_data *napid;
+	struct qca_napi_info *napii;
+
+	QDF_ASSERT(scn != NULL);
+
+	napid = hif_napi_get_all(hif_hdl);
+	if (scn != NULL) {
+		for (i = 0; i < scn->ce_count; i++) {
+			ce_state = scn->ce_id_to_state[i];
+			if ((ce_state != NULL) && (ce_state->htt_rx_data)) {
+				napii = &(napid->napis[i]);
+				HIF_ERROR("deRegistering LRO for ce_id %d NAPI callback for %d flush_cb %p, lro_data %p\n",
+					i, napii->id, napii->lro_flush_cb,
+					napii->lro_ctx);
+				qdf_spin_lock_bh(&napii->lro_unloading_lock);
+				napii->lro_flush_cb = NULL;
+				lro_deinit_cb(napii->lro_ctx);
+				napii->lro_ctx = NULL;
+				qdf_spin_unlock_bh(
+					&napii->lro_unloading_lock);
+				qdf_spinlock_destroy(
+					&napii->lro_unloading_lock);
+			}
+		}
+	} else {
+		HIF_ERROR("%s: hif_state NULL!", __func__);
+	}
+}
+
+/**
+ * hif_napi_get_lro_info() - returns the address LRO data for napi_id
+ * @hif: pointer to hif context
+ * @napi_id: napi instance
+ *
+ * Description:
+ *    Returns the address of the LRO structure
+ *
+ * Return:
+ *  <addr>: address of the LRO structure
+ */
+void *hif_napi_get_lro_info(struct hif_opaque_softc *hif_hdl, int napi_id)
+{
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
+	struct qca_napi_data *napid;
+	struct qca_napi_info *napii;
+
+	napid = &(scn->napi_data);
+	napii = &(napid->napis[NAPI_ID2PIPE(napi_id)]);
+
+	return napii->lro_ctx;
+}
+
 /**
  *
  * hif_napi_get_all() - returns the address of the whole HIF NAPI structure
@@ -612,9 +726,12 @@ int hif_napi_poll(struct hif_opaque_softc *hif_ctx, struct napi_struct *napi,
 	hif_record_ce_desc_event(hif, NAPI_ID2PIPE(napi_info->id),
 				 NAPI_POLL_ENTER, NULL, NULL, cpu);
 
-	if (unlikely(NULL == hif))
-		QDF_ASSERT(hif != NULL); /* emit a warning if hif NULL */
-	else {
+	qdf_spin_lock_bh(&napi_info->lro_unloading_lock);
+	if (unlikely(NULL == hif)) {
+		HIF_ERROR("%s: hif context is NULL", __func__);
+		QDF_ASSERT(0); /* emit a warning if hif NULL */
+		goto out;
+	} else {
 		rc = ce_per_engine_service(hif, NAPI_ID2PIPE(napi_info->id));
 		NAPI_DEBUG("%s: ce_per_engine_service processed %d msgs",
 			    __func__, rc);
@@ -622,11 +739,11 @@ int hif_napi_poll(struct hif_opaque_softc *hif_ctx, struct napi_struct *napi,
 	napi_info->stats[cpu].napi_workdone += rc;
 	normalized = (rc / napi_info->scale);
 
-	if (NULL != hif) {
-		ce_state = hif->ce_id_to_state[NAPI_ID2PIPE(napi_info->id)];
-		if (ce_state && ce_state->lro_flush_cb)
-			ce_state->lro_flush_cb(ce_state->lro_data);
-	}
+	ce_state = hif->ce_id_to_state[NAPI_ID2PIPE(napi_info->id)];
+
+	if (napi_info->lro_flush_cb)
+		napi_info->lro_flush_cb(napi_info->lro_ctx);
+	qdf_spin_unlock_bh(&napi_info->lro_unloading_lock);
 
 	/* do not return 0, if there was some work done,
 	 * even if it is below the scale
@@ -673,6 +790,8 @@ int hif_napi_poll(struct hif_opaque_softc *hif_ctx, struct napi_struct *napi,
 
 	NAPI_DEBUG("%s <--[normalized=%d]", __func__, normalized);
 	return normalized;
+out:
+	return rc;
 }
 
 #ifdef HELIUMPLUS

+ 4 - 1
qdf/linux/src/i_qdf_nbuf.h

@@ -137,7 +137,8 @@ struct qdf_nbuf_cb {
 				tcp_pure_ack:1,
 				ipv6_proto:1,
 				ip_offset:7,
-				tcp_offset:7;
+				tcp_offset:7,
+				rx_ctx_id:4;
 			uint32_t tcp_udp_chksum:16,
 				tcp_win:16;
 			uint32_t tcp_seq_num;
@@ -214,6 +215,8 @@ struct qdf_nbuf_cb {
 
 #define QDF_NBUF_CB_RX_LRO_ELIGIBLE(skb) \
 	(((struct qdf_nbuf_cb *)((skb)->cb))->u.rx.lro_eligible)
+#define QDF_NBUF_CB_RX_CTX_ID(skb) \
+	(((struct qdf_nbuf_cb *)((skb)->cb))->u.rx.rx_ctx_id)
 #define QDF_NBUF_CB_RX_TCP_PROTO(skb) \
 	(((struct qdf_nbuf_cb *)((skb)->cb))->u.rx.tcp_proto)
 #define QDF_NBUF_CB_RX_TCP_PURE_ACK(skb) \