瀏覽代碼

qcacld-3.0: Add vendor event to get the driver hang reason

Add Vendor Event to get the driver hang reason indicating to the
user space that the driver has detected an internal failure.
This event carries the information indicating the reason that triggered
this detection.

Change-Id: I3934f2a18c796ed3b53175dcbe7efd7f4d1409b9
CRs-fixed: 2098498
Anurag Chouhan 7 年之前
父節點
當前提交
4085ff7e51

+ 22 - 1
core/cds/inc/cds_api.h

@@ -354,7 +354,28 @@ bool cds_is_packet_log_enabled(void);
 
 uint64_t cds_get_monotonic_boottime(void);
 
-void cds_trigger_recovery(void);
+/**
+ * cds_get_recovery_reason() - get self recovery reason
+ * @reason: cds hang reason
+ *
+ * Return: None
+ */
+void cds_get_recovery_reason(enum qdf_hang_reason *reason);
+
+/**
+ * cds_reset_recovery_reason() - reset the reason to unspecified
+ *
+ * Return: None
+ */
+void cds_reset_recovery_reason(void);
+
+/**
+ * cds_trigger_recovery() - trigger self recovery
+ * @reason: recovery reason
+ *
+ * Return: none
+ */
+void cds_trigger_recovery(enum qdf_hang_reason reason);
 
 void cds_set_wakelock_logging(bool value);
 bool cds_is_wakelock_enabled(void);

+ 1 - 0
core/cds/inc/cds_sched.h

@@ -230,6 +230,7 @@ typedef struct _cds_context_type {
 	struct ol_tx_sched_wrr_ac_specs_t ac_specs[TX_WMM_AC_NUM];
 	qdf_work_t cds_recovery_work;
 	qdf_workqueue_t *cds_recovery_wq;
+	enum qdf_hang_reason recovery_reason;
 } cds_context_type, *p_cds_contextType;
 
 /*---------------------------------------------------------------------------

+ 39 - 1
core/cds/src/cds_api.c

@@ -1848,13 +1848,51 @@ deinit_rtl:
 	qdf_runtime_lock_deinit(&rtl);
 }
 
+/**
+ * cds_get_recovery_reason() - get self recovery reason
+ * @reason: recovery reason
+ *
+ * Return: None
+ */
+void cds_get_recovery_reason(enum qdf_hang_reason *reason)
+{
+	if (!gp_cds_context) {
+		cds_err("gp_cds_context is null");
+		return;
+	}
+
+	*reason = gp_cds_context->recovery_reason;
+}
+
+/**
+ * cds_reset_recovery_reason() - reset the reason to unspecified
+ *
+ * Return: None
+ */
+void cds_reset_recovery_reason(void)
+{
+	if (!gp_cds_context) {
+		cds_err("gp_cds_context is null");
+		return;
+	}
+
+	gp_cds_context->recovery_reason = QDF_REASON_UNSPECIFIED;
+}
+
 /**
  * cds_trigger_recovery() - trigger self recovery
+ * @reason: recovery reason
  *
  * Return: none
  */
-void cds_trigger_recovery(void)
+void cds_trigger_recovery(enum qdf_hang_reason reason)
 {
+	if (!gp_cds_context) {
+		cds_err("gp_cds_context is null");
+		return;
+	}
+
+	gp_cds_context->recovery_reason = reason;
 	if (in_atomic()) {
 		qdf_queue_work(0, gp_cds_context->cds_recovery_wq,
 				&gp_cds_context->cds_recovery_work);

+ 7 - 2
core/dp/txrx/ol_txrx.c

@@ -2463,6 +2463,9 @@ static void ol_txrx_flush_cache_rx_queue(void)
 	}
 }
 
+/* Define short name to use in cds_trigger_recovery */
+#define PEER_DEL_TIMEOUT QDF_PEER_DELETION_TIMEDOUT
+
 /**
  * ol_txrx_peer_attach - Allocate and set up references for a
  * data peer object.
@@ -2566,7 +2569,7 @@ ol_txrx_peer_attach(struct cdp_vdev *pvdev, uint8_t *peer_mac_addr)
 		if (QDF_STATUS_SUCCESS != rc) {
 			ol_txrx_err("error waiting for peer(%d) deletion, status %d\n",
 				    vdev->wait_on_peer_id, (int) rc);
-			cds_trigger_recovery();
+			cds_trigger_recovery(PEER_DEL_TIMEOUT);
 			vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID;
 
 			return NULL;
@@ -2673,6 +2676,8 @@ ol_txrx_peer_attach(struct cdp_vdev *pvdev, uint8_t *peer_mac_addr)
 	return (void *)peer;
 }
 
+#undef PEER_DEL_TIMEOUT
+
 /*
  * Discarding tx filter - removes all data frames (disconnected state)
  */
@@ -3457,7 +3462,7 @@ static QDF_STATUS ol_txrx_clear_peer(struct cdp_pdev *ppdev, uint8_t sta_id)
 void peer_unmap_timer_work_function(void *param)
 {
 	WMA_LOGE("Enter: %s", __func__);
-	cds_trigger_recovery();
+	cds_trigger_recovery(QDF_PEER_UNMAP_TIMEDOUT);
 }
 
 /**

+ 103 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -857,6 +857,105 @@ int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx,
 	return 0;
 }
 
+/*
+ * define short names for the global vendor params
+ * used by QCA_NL80211_VENDOR_SUBCMD_HANG
+ */
+#define HANG_REASON_INDEX QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX
+
+/**
+ * hdd_convert_hang_reason() - Convert cds recovery reason to vendor specific
+ * hang reason
+ * @reason: cds recovery reason
+ *
+ * Return: Vendor specific reason code
+ */
+static enum qca_wlan_vendor_hang_reason
+hdd_convert_hang_reason(enum qdf_hang_reason reason)
+{
+	uint32_t ret_val;
+
+	switch (reason) {
+	case QDF_RX_HASH_NO_ENTRY_FOUND:
+		ret_val = QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND;
+		break;
+	case QDF_PEER_DELETION_TIMEDOUT:
+		ret_val = QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT;
+		break;
+	case QDF_PEER_UNMAP_TIMEDOUT:
+		ret_val = QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT;
+		break;
+	case QDF_SCAN_REQ_EXPIRED:
+		ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED;
+		break;
+	case QDF_SCAN_ATTEMPT_FAILURES:
+		ret_val = QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES;
+		break;
+	case QDF_GET_MSG_BUFF_FAILURE:
+		ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE;
+		break;
+	case QDF_ACTIVE_LIST_TIMEOUT:
+		ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT;
+		break;
+	case QDF_SUSPEND_TIMEOUT:
+		ret_val = QCA_WLAN_HANG_SUSPEND_TIMEOUT;
+		break;
+	case QDF_RESUME_TIMEOUT:
+		ret_val = QCA_WLAN_HANG_RESUME_TIMEOUT;
+		break;
+	case QDF_REASON_UNSPECIFIED:
+	default:
+		ret_val = QCA_WLAN_HANG_REASON_UNSPECIFIED;
+	}
+	return ret_val;
+}
+
+/**
+ * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace
+ * @hdd_ctx: Pointer to hdd context
+ * @reason: cds recovery reason
+ *
+ * Return: 0 on success or failure reason
+ */
+int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx,
+				    enum qdf_hang_reason reason)
+{
+	struct sk_buff *vendor_event;
+	enum qca_wlan_vendor_hang_reason hang_reason;
+
+	ENTER();
+
+	if (!hdd_ctx) {
+		hdd_err("HDD context is null");
+		return -EINVAL;
+	}
+
+	vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
+						   NULL,
+						   sizeof(uint32_t),
+						   HANG_REASON_INDEX,
+						   GFP_KERNEL);
+	if (!vendor_event) {
+		hdd_err("cfg80211_vendor_event_alloc failed");
+		return -ENOMEM;
+	}
+
+	hang_reason = hdd_convert_hang_reason(reason);
+
+	if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON,
+			(uint32_t) hang_reason)) {
+		hdd_err("QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail");
+		kfree_skb(vendor_event);
+		return -EINVAL;
+	}
+
+	cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+
+	EXIT();
+	return 0;
+}
+#undef HANG_REASON_INDEX
+
 /**
  * wlan_hdd_get_adjacent_chan(): Gets next/previous channel
  * with respect to the channel passed.
@@ -1317,6 +1416,10 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
 		.vendor_id = QCA_NL80211_VENDOR_ID,
 		.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET,
 	},
+	[QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX] = {
+		.vendor_id = QCA_NL80211_VENDOR_ID,
+		.subcmd = QCA_NL80211_VENDOR_SUBCMD_HANG,
+	},
 
 #ifdef WLAN_UMAC_CONVERGENCE
 	COMMON_VENDOR_EVENTS

+ 9 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -326,6 +326,15 @@ void wlan_hdd_testmode_rx_event(void *buf, size_t buf_len);
 int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx,
 				   struct ch_avoid_ind_type *avoid_freq_list);
 
+/**
+ * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace
+ * @hdd_ctx: Pointer to hdd context
+ * @reason: cds recovery reason
+ *
+ * Return: 0 on success or failure reason
+ */
+int wlan_hdd_send_hang_reason_event(struct hdd_context *pHddCtx, uint32_t reason);
+
 int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, uint8_t op_chan);
 
 #ifdef FEATURE_WLAN_EXTSCAN

+ 19 - 0
core/hdd/src/wlan_hdd_driver_ops.c

@@ -473,6 +473,24 @@ static inline void hdd_wlan_ssr_shutdown_event(void)
 };
 #endif
 
+/**
+ * hdd_send_hang_reason() - Send hang reason to the userspace
+ *
+ * Return: None
+ */
+static void hdd_send_hang_reason(void)
+{
+	uint32_t reason = 0;
+	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return;
+
+	cds_get_recovery_reason(&reason);
+	cds_reset_recovery_reason();
+	wlan_hdd_send_hang_reason_event(hdd_ctx, reason);
+}
+
 /**
  * wlan_hdd_shutdown() - wlan_hdd_shutdown
  *
@@ -505,6 +523,7 @@ static void wlan_hdd_shutdown(void)
 	/* this is for cases, where shutdown invoked from platform */
 	cds_set_recovery_in_progress(true);
 	hdd_wlan_ssr_shutdown_event();
+	hdd_send_hang_reason();
 
 	if (!cds_wait_for_external_threads_completion(__func__))
 		hdd_err("Host is not ready for SSR, attempting anyway");

+ 1 - 1
core/hdd/src/wlan_hdd_hostapd.c

@@ -2929,7 +2929,7 @@ static int __iw_softap_set_two_ints_getnone(struct net_device *dev,
 			return 0;
 		}
 		if (value[1] == 3) {
-			cds_trigger_recovery();
+			cds_trigger_recovery(QDF_REASON_UNSPECIFIED);
 			return 0;
 		}
 		ret = wma_cli_set2_command(adapter->sessionId,

+ 1 - 1
core/hdd/src/wlan_hdd_main.c

@@ -12686,7 +12686,7 @@ void hdd_drv_ops_inactivity_handler(void)
 		QDF_BUG(0);
 
 	if (cds_is_self_recovery_enabled())
-		cds_trigger_recovery();
+		cds_trigger_recovery(QDF_REASON_UNSPECIFIED);
 	else
 		QDF_BUG(0);
 }

+ 5 - 1
core/hdd/src/wlan_hdd_scan.c

@@ -419,6 +419,9 @@ static int wlan_hdd_update_scan_ies(struct hdd_adapter *adapter,
 	return 0;
 }
 
+/* Define short name to use in cds_trigger_recovery */
+#define SCAN_FAILURE QDF_SCAN_ATTEMPT_FAILURES
+
 /**
  * __wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request
  * @wiphy: Pointer to wiphy
@@ -570,7 +573,7 @@ static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy,
 					   hdd_ctx->config->enableSelfRecovery);
 				} else {
 					hdd_err("Triggering SSR due to scan stuck");
-					cds_trigger_recovery();
+					cds_trigger_recovery(SCAN_FAILURE);
 				}
 			}
 		}
@@ -880,6 +883,7 @@ free_mem:
 #endif
 
 }
+#undef SCAN_FAILURE
 
 /**
  * wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request

+ 1 - 1
core/hdd/src/wlan_hdd_wext.c

@@ -13065,7 +13065,7 @@ static int __iw_set_two_ints_getnone(struct net_device *dev,
 			return 0;
 		}
 		if (value[1] == 3) {
-			cds_trigger_recovery();
+			cds_trigger_recovery(QDF_REASON_UNSPECIFIED);
 			return 0;
 		}
 		ret = wma_cli_set2_command(adapter->sessionId,

+ 1 - 1
core/sme/src/common/sme_api.c

@@ -462,7 +462,7 @@ tSmeCmd *sme_get_command_buffer(tpAniSirGlobal pMac)
 				false,
 				pMac->sme.enableSelfRecovery ? true : false);
 		else
-			cds_trigger_recovery();
+			cds_trigger_recovery(QDF_GET_MSG_BUFF_FAILURE);
 	}
 
 	/* memset to zero */

+ 1 - 1
core/wma/src/wma_features.c

@@ -3928,7 +3928,7 @@ static inline void wma_suspend_target_timeout(bool is_self_recovery_enabled)
 		WMA_LOGE("%s: Module (un)loading; Ignoring suspend timeout",
 			 __func__);
 	else
-		cds_trigger_recovery();
+		cds_trigger_recovery(QDF_SUSPEND_TIMEOUT);
 }
 
 #ifdef FEATURE_WLAN_TDLS