All of lore.kernel.org
 help / color / mirror / Atom feed
* [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery
       [not found] <8c0a4f68cc3454b77a21ab949075fb0632549bb9>
@ 2013-06-25  8:13 ` Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery Michal Kazior
                     ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: Michal Kazior @ 2013-06-25  8:13 UTC (permalink / raw)
  To: ath9k-devel

Respin of hw recovery. I've addressed some issues
raised in the previous review.

I haven't found a way to address the 'scattered
logic' issue (i.e. the way restart_work is
handled).

Michal Kazior (3):
  ath10k: implement device recovery
  ath10k: implement fw crash simulation command
  ath10k: create debugfs interface to trigger fw crash

 drivers/net/wireless/ath/ath10k/core.c  |   30 +++++++++
 drivers/net/wireless/ath/ath10k/core.h  |   16 +++++
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++
 drivers/net/wireless/ath/ath10k/htc.c   |    3 +
 drivers/net/wireless/ath/ath10k/mac.c   |  101 +++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h   |    1 +
 drivers/net/wireless/ath/ath10k/pci.c   |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c   |   26 ++++++++
 drivers/net/wireless/ath/ath10k/wmi.h   |   19 ++++++
 9 files changed, 233 insertions(+), 19 deletions(-)

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery
  2013-06-25  8:13 ` [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery Michal Kazior
@ 2013-06-25  8:13   ` Michal Kazior
  2013-07-05  7:47     ` Kalle Valo
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 2/3] ath10k: implement fw crash simulation command Michal Kazior
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 26+ messages in thread
From: Michal Kazior @ 2013-06-25  8:13 UTC (permalink / raw)
  To: ath9k-devel

Restart the hardware if FW crashes.

If FW crashes during recovery we leave the
hardware in a "wedged" state to avoid recursive
recoveries.

When in "wedged" state userspace may bring
interfaces down (to issue stop()) and then bring
one interface (to issue start()) to reload
hardware manually.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   30 ++++++++++
 drivers/net/wireless/ath/ath10k/core.h |   16 +++++
 drivers/net/wireless/ath/ath10k/htc.c  |    3 +
 drivers/net/wireless/ath/ath10k/mac.c  |  101 ++++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h  |    1 +
 drivers/net/wireless/ath/ath10k/pci.c  |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c  |    7 +++
 7 files changed, 141 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index c37f79f..503b39f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar)
 	return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+	mutex_lock(&ar->conf_mutex);
+
+	switch (ar->state) {
+	case ATH10K_STATE_ON:
+		ath10k_halt(ar);
+		ar->state = ATH10K_STATE_RESTARTING_OFF;
+		ieee80211_restart_hw(ar->hw);
+		break;
+	case ATH10K_STATE_OFF:
+		/* this can happen if driver is being unloaded */
+		ath10k_warn("cannot restart a device that hasn't been started\n");
+		break;
+	case ATH10K_STATE_RESTARTING_OFF:
+	case ATH10K_STATE_RESTARTING_ON:
+		ar->state = ATH10K_STATE_WEDGED;
+		/* fall through */
+	case ATH10K_STATE_WEDGED:
+		ath10k_warn("device is wedged, will not restart\n");
+		break;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops)
 {
@@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
 	init_waitqueue_head(&ar->event_queue);
 
+	INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
 	return ar;
 
 err_wq:
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 413f1c5..cf8a3b3 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -250,6 +250,20 @@ struct ath10k_debug {
 enum ath10k_state {
 	ATH10K_STATE_OFF = 0,
 	ATH10K_STATE_ON,
+
+	/* When doing firmware recovery the device is first powered down.
+	 * mac80211 is supposed to call in to start() hook later on. It is
+	 * however possible that driver unloading and firmware crash overlap.
+	 * mac80211 can wait on conf_mutex in stop() while the device is
+	 * stopped in ath10k_core_restart() work holding conf_mutex. */
+	ATH10K_STATE_RESTARTING_OFF,
+	ATH10K_STATE_RESTARTING_ON,
+
+	/* The device has crashed while restarting hw. This state is like ON
+	 * but commands are blocked in HTC and -ECOMM response is given. This
+	 * prevents completion timeouts and makes the driver more responsive to
+	 * userspace commands. This is also prevents recursive recovery. */
+	ATH10K_STATE_WEDGED,
 };
 
 struct ath10k {
@@ -355,6 +369,8 @@ struct ath10k {
 
 	enum ath10k_state state;
 
+	struct work_struct restart_work;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 7d5a366..72e072c 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
 	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+	if (htc->ar->state == ATH10K_STATE_WEDGED)
+		return -ECOMM;
+
 	if (eid >= ATH10K_HTC_EP_COUNT) {
 		ath10k_warn("Invalid endpoint id: %d\n", eid);
 		return -ENOENT;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 8989d06..90502dc 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1735,7 +1735,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
-static void ath10k_halt(struct ath10k *ar)
+void ath10k_halt(struct ath10k *ar)
 {
 	lockdep_assert_held(&ar->conf_mutex);
 
@@ -1761,7 +1761,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 
-	if (ar->state != ATH10K_STATE_OFF) {
+	if (ar->state != ATH10K_STATE_OFF &&
+	    ar->state != ATH10K_STATE_RESTARTING_OFF) {
 		ret = -EINVAL;
 		goto exit;
 	}
@@ -1781,6 +1782,11 @@ static int ath10k_start(struct ieee80211_hw *hw)
 		goto exit;
 	}
 
+	if (ar->state == ATH10K_STATE_OFF)
+		ar->state = ATH10K_STATE_ON;
+	else if (ar->state == ATH10K_STATE_RESTARTING_OFF)
+		ar->state = ATH10K_STATE_RESTARTING_ON;
+
 	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
 	if (ret)
 		ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1803,22 +1809,47 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 	struct ath10k *ar = hw->priv;
 
 	mutex_lock(&ar->conf_mutex);
-	if (ar->state == ATH10K_STATE_ON)
+	if (ar->state == ATH10K_STATE_ON ||
+	    ar->state == ATH10K_STATE_RESTARTING_ON ||
+	    ar->state == ATH10K_STATE_WEDGED)
 		ath10k_halt(ar);
 
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
+	cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
 	struct ath10k_generic_iter ar_iter;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar->state == ATH10K_STATE_RESTARTING_ON)
+		return;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	ieee80211_iterate_active_interfaces_atomic(
+		ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+		ath10k_ps_iter, &ar_iter);
+
+	if (ar_iter.ret)
+		ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
 	struct ath10k *ar = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
 	int ret = 0;
-	u32 flags;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -1830,18 +1861,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 		spin_unlock_bh(&ar->data_lock);
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_PS) {
-		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-		ar_iter.ar = ar;
-		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-		ieee80211_iterate_active_interfaces_atomic(hw,
-							   flags,
-							   ath10k_ps_iter,
-							   &ar_iter);
-
-		ret = ar_iter.ret;
-	}
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		ath10k_config_ps(ar);
 
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
 		if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1850,6 +1871,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 			ret = ath10k_monitor_destroy(ar);
 	}
 
+	ath10k_wmi_flush_tx(ar);
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
@@ -2692,6 +2714,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTING_ON)
+		return;
+
 	rts = min_t(u32, rts, ATH10K_RTS_MAX);
 
 	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2732,6 +2761,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTING_ON)
+		return;
+
 	frag = clamp_t(u32, frag,
 		       ATH10K_FRAGMT_THRESHOLD_MIN,
 		       ATH10K_FRAGMT_THRESHOLD_MAX);
@@ -2770,6 +2806,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
 	struct ath10k *ar = hw->priv;
+	bool skip;
 	int ret;
 
 	/* mac80211 doesn't care if we really xmit queued frames or not
@@ -2779,17 +2816,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 
 	mutex_lock(&ar->conf_mutex);
 
+	if (ar->state == ATH10K_STATE_WEDGED)
+		goto skip;
+
 	ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
 			bool empty;
+
 			spin_lock_bh(&ar->htt.tx_lock);
 			empty = bitmap_empty(ar->htt.used_msdu_ids,
 					     ar->htt.max_num_pending_tx);
 			spin_unlock_bh(&ar->htt.tx_lock);
-			(empty);
+
+			skip = (ar->state == ATH10K_STATE_WEDGED);
+
+			(empty || skip);
 		}), ATH10K_FLUSH_TIMEOUT_HZ);
-	if (ret <= 0)
+
+	if (ret <= 0 || skip)
 		ath10k_warn("tx not flushed\n");
 
+skip:
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -2863,6 +2909,22 @@ static int ath10k_resume(struct ieee80211_hw *hw)
 }
 #endif
 
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	mutex_lock(&ar->conf_mutex);
+
+	/* If device failed to restart it will be in a different state, e.g.
+	 * ATH10K_STATE_WEDGED */
+	if (ar->state == ATH10K_STATE_RESTARTING_ON) {
+		ath10k_info("device successfully recovered\n");
+		ar->state = ATH10K_STATE_ON;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_tx,
 	.start				= ath10k_start,
@@ -2883,6 +2945,7 @@ static const struct ieee80211_ops ath10k_ops = {
 	.set_frag_threshold		= ath10k_set_frag_threshold,
 	.flush				= ath10k_flush,
 	.tx_last_beacon			= ath10k_tx_last_beacon,
+	.restart_complete		= ath10k_restart_complete,
 #ifdef CONFIG_PM
 	.suspend			= ath10k_suspend,
 	.resume				= ath10k_resume,
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 27fc92e..6fce9bf 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 3790678..e60a927 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
 			   reg_dump_values[i + 1],
 			   reg_dump_values[i + 2],
 			   reg_dump_values[i + 3]);
+
+	ieee80211_queue_work(ar->hw, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index b7e7e45..0d25cd7 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
 {
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (ar->state == ATH10K_STATE_WEDGED) {
+		ath10k_warn("wmi flush skipped - device is wedged anyway\n");
+		return;
+	}
+
 	ret = wait_event_timeout(ar->wmi.wq,
 				 atomic_read(&ar->wmi.pending_tx_count) == 0,
 				 5*HZ);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 2/3] ath10k: implement fw crash simulation command
  2013-06-25  8:13 ` [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery Michal Kazior
@ 2013-06-25  8:13   ` Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 3/3] ath10k: create debugfs interface to trigger fw crash Michal Kazior
  2013-07-16  7:54     ` Michal Kazior
  3 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-06-25  8:13 UTC (permalink / raw)
  To: ath9k-devel

This can be useful to test FW crash handling.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/wmi.c |   19 +++++++++++++++++++
 drivers/net/wireless/ath/ath10k/wmi.h |   19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 0d25cd7..5e42460 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2092,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
 	return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
 }
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+	struct wmi_force_fw_hang_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+	cmd->type = __cpu_to_le32(type);
+	cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+		   type, delay_ms);
+	return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 9555f5a..da3b2bc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -416,6 +416,7 @@ enum wmi_cmd_id {
 	WMI_PDEV_FTM_INTG_CMDID,
 	WMI_VDEV_SET_KEEPALIVE_CMDID,
 	WMI_VDEV_GET_KEEPALIVE_CMDID,
+	WMI_FORCE_FW_HANG_CMDID,
 
 	/* GPIO Configuration */
 	WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
 	struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+	WMI_FORCE_FW_HANG_ASSERT = 1,
+	WMI_FORCE_FW_HANG_NO_DETECT,
+	WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+	WMI_FORCE_FW_HANG_EMPTY_POINT,
+	WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+	WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+	__le32 type;
+	__le32 delay_ms;
+} __packed;
+
 #define ATH10K_RTS_MAX		2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN	540
 #define ATH10K_FRAGMT_THRESHOLD_MAX	2346
@@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 			const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms);
 
 #endif /* _WMI_H_ */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 3/3] ath10k: create debugfs interface to trigger fw crash
  2013-06-25  8:13 ` [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 2/3] ath10k: implement fw crash simulation command Michal Kazior
@ 2013-06-25  8:13   ` Michal Kazior
  2013-07-16  7:54     ` Michal Kazior
  3 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-06-25  8:13 UTC (permalink / raw)
  To: ath9k-devel

This can be useful for testing.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 65279f5..e18f988 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -445,6 +445,57 @@ static const struct file_operations fops_fw_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTING_ON) {
+		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return count;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -461,6 +512,9 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
 	return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery Michal Kazior
@ 2013-07-05  7:47     ` Kalle Valo
  2013-07-05  8:05       ` Michal Kazior
  0 siblings, 1 reply; 26+ messages in thread
From: Kalle Valo @ 2013-07-05  7:47 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> Restart the hardware if FW crashes.
>
> If FW crashes during recovery we leave the
> hardware in a "wedged" state to avoid recursive
> recoveries.
>
> When in "wedged" state userspace may bring
> interfaces down (to issue stop()) and then bring
> one interface (to issue start()) to reload
> hardware manually.
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

[...]

> --- a/drivers/net/wireless/ath/ath10k/core.h
> +++ b/drivers/net/wireless/ath/ath10k/core.h
> @@ -250,6 +250,20 @@ struct ath10k_debug {
>  enum ath10k_state {
>  	ATH10K_STATE_OFF = 0,
>  	ATH10K_STATE_ON,
> +
> +	/* When doing firmware recovery the device is first powered down.
> +	 * mac80211 is supposed to call in to start() hook later on. It is
> +	 * however possible that driver unloading and firmware crash overlap.
> +	 * mac80211 can wait on conf_mutex in stop() while the device is
> +	 * stopped in ath10k_core_restart() work holding conf_mutex. */
> +	ATH10K_STATE_RESTARTING_OFF,
> +	ATH10K_STATE_RESTARTING_ON,

I feel that RESTARTING_OFF and RESTARTING_ON are a bit confusing names,
especially when we have OFF and ON states. Wouldn't RESTARTING and
RESTARTED be simpler?

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery
  2013-07-05  7:47     ` Kalle Valo
@ 2013-07-05  8:05       ` Michal Kazior
  2013-07-15  7:19           ` Kalle Valo
  0 siblings, 1 reply; 26+ messages in thread
From: Michal Kazior @ 2013-07-05  8:05 UTC (permalink / raw)
  To: ath9k-devel

On 5 July 2013 09:47, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
> Michal Kazior <michal.kazior@tieto.com> writes:
>
>> Restart the hardware if FW crashes.
>>
>> If FW crashes during recovery we leave the
>> hardware in a "wedged" state to avoid recursive
>> recoveries.
>>
>> When in "wedged" state userspace may bring
>> interfaces down (to issue stop()) and then bring
>> one interface (to issue start()) to reload
>> hardware manually.
>>
>> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
>
> [...]
>
>> --- a/drivers/net/wireless/ath/ath10k/core.h
>> +++ b/drivers/net/wireless/ath/ath10k/core.h
>> @@ -250,6 +250,20 @@ struct ath10k_debug {
>>  enum ath10k_state {
>>       ATH10K_STATE_OFF = 0,
>>       ATH10K_STATE_ON,
>> +
>> +     /* When doing firmware recovery the device is first powered down.
>> +      * mac80211 is supposed to call in to start() hook later on. It is
>> +      * however possible that driver unloading and firmware crash overlap.
>> +      * mac80211 can wait on conf_mutex in stop() while the device is
>> +      * stopped in ath10k_core_restart() work holding conf_mutex. */
>> +     ATH10K_STATE_RESTARTING_OFF,
>> +     ATH10K_STATE_RESTARTING_ON,
>
> I feel that RESTARTING_OFF and RESTARTING_ON are a bit confusing names,
> especially when we have OFF and ON states. Wouldn't RESTARTING and
> RESTARTED be simpler?

RESTARTING/RESTARTED seems confusing to me too. The whole thing
shouldn't be considered restarted until mac80211 calls in
restart_complete() - but then again, at that point we should consider
the state as STATE_ON (and we do). Having RESTARTING_OFF / _ON gives
off more info about the state hw is in.

Ideally we should have only one state - RESTARTING and guarantee that
the hw is in a workable state before we call in for
ieee80211_restart_hw(). We'd need to skip hw startup in ath10k_start()
in that case and make some code from ath10k_start() reusable and
callable from ath10k_core_start().

I can't really remember the reason I went with this particular design
now. Perhaps this can be addressed in follow up patches later?


Pozdrawiam / Best regards,
Micha? Kazior.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery
  2013-07-05  8:05       ` Michal Kazior
@ 2013-07-15  7:19           ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-15  7:19 UTC (permalink / raw)
  To: ath9k-devel

Hi Michal,

sorry, I forgot to answer this one.

Michal Kazior <michal.kazior@tieto.com> writes:

> On 5 July 2013 09:47, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
>
>> I feel that RESTARTING_OFF and RESTARTING_ON are a bit confusing names,
>> especially when we have OFF and ON states. Wouldn't RESTARTING and
>> RESTARTED be simpler?
>
> RESTARTING/RESTARTED seems confusing to me too. The whole thing
> shouldn't be considered restarted until mac80211 calls in
> restart_complete() - but then again, at that point we should consider
> the state as STATE_ON (and we do). Having RESTARTING_OFF / _ON gives
> off more info about the state hw is in.
>
> Ideally we should have only one state - RESTARTING and guarantee that
> the hw is in a workable state before we call in for
> ieee80211_restart_hw(). We'd need to skip hw startup in ath10k_start()
> in that case and make some code from ath10k_start() reusable and
> callable from ath10k_core_start().
>
> I can't really remember the reason I went with this particular design
> now. Perhaps this can be addressed in follow up patches later?

I would prefer that you still rename the states to what I suggested.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery
@ 2013-07-15  7:19           ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-15  7:19 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath9k-devel, ath10k

Hi Michal,

sorry, I forgot to answer this one.

Michal Kazior <michal.kazior@tieto.com> writes:

> On 5 July 2013 09:47, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
>
>> I feel that RESTARTING_OFF and RESTARTING_ON are a bit confusing names,
>> especially when we have OFF and ON states. Wouldn't RESTARTING and
>> RESTARTED be simpler?
>
> RESTARTING/RESTARTED seems confusing to me too. The whole thing
> shouldn't be considered restarted until mac80211 calls in
> restart_complete() - but then again, at that point we should consider
> the state as STATE_ON (and we do). Having RESTARTING_OFF / _ON gives
> off more info about the state hw is in.
>
> Ideally we should have only one state - RESTARTING and guarantee that
> the hw is in a workable state before we call in for
> ieee80211_restart_hw(). We'd need to skip hw startup in ath10k_start()
> in that case and make some code from ath10k_start() reusable and
> callable from ath10k_core_start().
>
> I can't really remember the reason I went with this particular design
> now. Perhaps this can be addressed in follow up patches later?

I would prefer that you still rename the states to what I suggested.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v3 0/3] ath10k: hardware recovery
  2013-06-25  8:13 ` [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery Michal Kazior
@ 2013-07-16  7:54     ` Michal Kazior
  2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 2/3] ath10k: implement fw crash simulation command Michal Kazior
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Respin of hw recovery. This implements recovery
from a firmware crash.

Note: this is based on my
      "ath10k: device setup refactor" patchset.

v3:
 * rename RESTARTING_ON/OFF to RESTARTING/RESTARTED
 * update commit message for 'create debufs..'

Michal Kazior (3):
  ath10k: implement device recovery
  ath10k: implement fw crash simulation command
  ath10k: create debugfs interface to trigger fw crash

 drivers/net/wireless/ath/ath10k/core.c  |   30 +++++++++
 drivers/net/wireless/ath/ath10k/core.h  |   19 ++++++
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++
 drivers/net/wireless/ath/ath10k/htc.c   |    3 +
 drivers/net/wireless/ath/ath10k/mac.c   |  101 +++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h   |    1 +
 drivers/net/wireless/ath/ath10k/pci.c   |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c   |   26 ++++++++
 drivers/net/wireless/ath/ath10k/wmi.h   |   19 ++++++
 9 files changed, 236 insertions(+), 19 deletions(-)

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v3 0/3] ath10k: hardware recovery
@ 2013-07-16  7:54     ` Michal Kazior
  0 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Respin of hw recovery. This implements recovery
from a firmware crash.

Note: this is based on my
      "ath10k: device setup refactor" patchset.

v3:
 * rename RESTARTING_ON/OFF to RESTARTING/RESTARTED
 * update commit message for 'create debufs..'

Michal Kazior (3):
  ath10k: implement device recovery
  ath10k: implement fw crash simulation command
  ath10k: create debugfs interface to trigger fw crash

 drivers/net/wireless/ath/ath10k/core.c  |   30 +++++++++
 drivers/net/wireless/ath/ath10k/core.h  |   19 ++++++
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++
 drivers/net/wireless/ath/ath10k/htc.c   |    3 +
 drivers/net/wireless/ath/ath10k/mac.c   |  101 +++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h   |    1 +
 drivers/net/wireless/ath/ath10k/pci.c   |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c   |   26 ++++++++
 drivers/net/wireless/ath/ath10k/wmi.h   |   19 ++++++
 9 files changed, 236 insertions(+), 19 deletions(-)

-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v3 1/3] ath10k: implement device recovery
  2013-07-16  7:54     ` Michal Kazior
@ 2013-07-16  7:54       ` Michal Kazior
  -1 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Restart the hardware if FW crashes.

If FW crashes during recovery we leave the
hardware in a "wedged" state to avoid recursive
recoveries.

When in "wedged" state userspace may bring
interfaces down (to issue stop()) and then bring
one interface (to issue start()) to reload
hardware manually.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   30 ++++++++++
 drivers/net/wireless/ath/ath10k/core.h |   19 ++++++
 drivers/net/wireless/ath/ath10k/htc.c  |    3 +
 drivers/net/wireless/ath/ath10k/mac.c  |  101 ++++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h  |    1 +
 drivers/net/wireless/ath/ath10k/pci.c  |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c  |    7 +++
 7 files changed, 144 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index c37f79f..7226c23 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar)
 	return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+	mutex_lock(&ar->conf_mutex);
+
+	switch (ar->state) {
+	case ATH10K_STATE_ON:
+		ath10k_halt(ar);
+		ar->state = ATH10K_STATE_RESTARTING;
+		ieee80211_restart_hw(ar->hw);
+		break;
+	case ATH10K_STATE_OFF:
+		/* this can happen if driver is being unloaded */
+		ath10k_warn("cannot restart a device that hasn't been started\n");
+		break;
+	case ATH10K_STATE_RESTARTING:
+	case ATH10K_STATE_RESTARTED:
+		ar->state = ATH10K_STATE_WEDGED;
+		/* fall through */
+	case ATH10K_STATE_WEDGED:
+		ath10k_warn("device is wedged, will not restart\n");
+		break;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops)
 {
@@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
 	init_waitqueue_head(&ar->event_queue);
 
+	INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
 	return ar;
 
 err_wq:
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 413f1c5..9f21ecb 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -250,6 +250,23 @@ struct ath10k_debug {
 enum ath10k_state {
 	ATH10K_STATE_OFF = 0,
 	ATH10K_STATE_ON,
+
+	/* When doing firmware recovery the device is first powered down.
+	 * mac80211 is supposed to call in to start() hook later on. It is
+	 * however possible that driver unloading and firmware crash overlap.
+	 * mac80211 can wait on conf_mutex in stop() while the device is
+	 * stopped in ath10k_core_restart() work holding conf_mutex. The state
+	 * RESTARTED means that the device is up and mac80211 has started hw
+	 * reconfiguration. Once mac80211 is done with the reconfiguration we
+	 * set the state to STATE_ON in restart_complete(). */
+	ATH10K_STATE_RESTARTING,
+	ATH10K_STATE_RESTARTED,
+
+	/* The device has crashed while restarting hw. This state is like ON
+	 * but commands are blocked in HTC and -ECOMM response is given. This
+	 * prevents completion timeouts and makes the driver more responsive to
+	 * userspace commands. This is also prevents recursive recovery. */
+	ATH10K_STATE_WEDGED,
 };
 
 struct ath10k {
@@ -355,6 +372,8 @@ struct ath10k {
 
 	enum ath10k_state state;
 
+	struct work_struct restart_work;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 7d5a366..72e072c 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
 	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+	if (htc->ar->state == ATH10K_STATE_WEDGED)
+		return -ECOMM;
+
 	if (eid >= ATH10K_HTC_EP_COUNT) {
 		ath10k_warn("Invalid endpoint id: %d\n", eid);
 		return -ENOENT;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 2dfd446..b1bb318 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1738,7 +1738,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
-static void ath10k_halt(struct ath10k *ar)
+void ath10k_halt(struct ath10k *ar)
 {
 	lockdep_assert_held(&ar->conf_mutex);
 
@@ -1764,7 +1764,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 
-	if (ar->state != ATH10K_STATE_OFF) {
+	if (ar->state != ATH10K_STATE_OFF &&
+	    ar->state != ATH10K_STATE_RESTARTING) {
 		ret = -EINVAL;
 		goto exit;
 	}
@@ -1784,6 +1785,11 @@ static int ath10k_start(struct ieee80211_hw *hw)
 		goto exit;
 	}
 
+	if (ar->state == ATH10K_STATE_OFF)
+		ar->state = ATH10K_STATE_ON;
+	else if (ar->state == ATH10K_STATE_RESTARTING)
+		ar->state = ATH10K_STATE_RESTARTED;
+
 	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
 	if (ret)
 		ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1806,22 +1812,47 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 	struct ath10k *ar = hw->priv;
 
 	mutex_lock(&ar->conf_mutex);
-	if (ar->state == ATH10K_STATE_ON)
+	if (ar->state == ATH10K_STATE_ON ||
+	    ar->state == ATH10K_STATE_RESTARTED ||
+	    ar->state == ATH10K_STATE_WEDGED)
 		ath10k_halt(ar);
 
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
+	cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
 	struct ath10k_generic_iter ar_iter;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	ieee80211_iterate_active_interfaces_atomic(
+		ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+		ath10k_ps_iter, &ar_iter);
+
+	if (ar_iter.ret)
+		ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
 	struct ath10k *ar = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
 	int ret = 0;
-	u32 flags;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -1833,18 +1864,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 		spin_unlock_bh(&ar->data_lock);
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_PS) {
-		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-		ar_iter.ar = ar;
-		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-		ieee80211_iterate_active_interfaces_atomic(hw,
-							   flags,
-							   ath10k_ps_iter,
-							   &ar_iter);
-
-		ret = ar_iter.ret;
-	}
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		ath10k_config_ps(ar);
 
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
 		if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1853,6 +1874,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 			ret = ath10k_monitor_destroy(ar);
 	}
 
+	ath10k_wmi_flush_tx(ar);
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
@@ -2695,6 +2717,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
 	rts = min_t(u32, rts, ATH10K_RTS_MAX);
 
 	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2735,6 +2764,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
 	frag = clamp_t(u32, frag,
 		       ATH10K_FRAGMT_THRESHOLD_MIN,
 		       ATH10K_FRAGMT_THRESHOLD_MAX);
@@ -2773,6 +2809,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
 	struct ath10k *ar = hw->priv;
+	bool skip;
 	int ret;
 
 	/* mac80211 doesn't care if we really xmit queued frames or not
@@ -2782,17 +2819,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 
 	mutex_lock(&ar->conf_mutex);
 
+	if (ar->state == ATH10K_STATE_WEDGED)
+		goto skip;
+
 	ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
 			bool empty;
+
 			spin_lock_bh(&ar->htt.tx_lock);
 			empty = bitmap_empty(ar->htt.used_msdu_ids,
 					     ar->htt.max_num_pending_tx);
 			spin_unlock_bh(&ar->htt.tx_lock);
-			(empty);
+
+			skip = (ar->state == ATH10K_STATE_WEDGED);
+
+			(empty || skip);
 		}), ATH10K_FLUSH_TIMEOUT_HZ);
-	if (ret <= 0)
+
+	if (ret <= 0 || skip)
 		ath10k_warn("tx not flushed\n");
 
+skip:
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -2866,6 +2912,22 @@ static int ath10k_resume(struct ieee80211_hw *hw)
 }
 #endif
 
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	mutex_lock(&ar->conf_mutex);
+
+	/* If device failed to restart it will be in a different state, e.g.
+	 * ATH10K_STATE_WEDGED */
+	if (ar->state == ATH10K_STATE_RESTARTED) {
+		ath10k_info("device successfully recovered\n");
+		ar->state = ATH10K_STATE_ON;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_tx,
 	.start				= ath10k_start,
@@ -2886,6 +2948,7 @@ static const struct ieee80211_ops ath10k_ops = {
 	.set_frag_threshold		= ath10k_set_frag_threshold,
 	.flush				= ath10k_flush,
 	.tx_last_beacon			= ath10k_tx_last_beacon,
+	.restart_complete		= ath10k_restart_complete,
 #ifdef CONFIG_PM
 	.suspend			= ath10k_suspend,
 	.resume				= ath10k_resume,
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 27fc92e..6fce9bf 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index bfe8561..c71b488 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
 			   reg_dump_values[i + 1],
 			   reg_dump_values[i + 2],
 			   reg_dump_values[i + 3]);
+
+	ieee80211_queue_work(ar->hw, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index b7e7e45..0d25cd7 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
 {
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (ar->state == ATH10K_STATE_WEDGED) {
+		ath10k_warn("wmi flush skipped - device is wedged anyway\n");
+		return;
+	}
+
 	ret = wait_event_timeout(ar->wmi.wq,
 				 atomic_read(&ar->wmi.pending_tx_count) == 0,
 				 5*HZ);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 1/3] ath10k: implement device recovery
@ 2013-07-16  7:54       ` Michal Kazior
  0 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Restart the hardware if FW crashes.

If FW crashes during recovery we leave the
hardware in a "wedged" state to avoid recursive
recoveries.

When in "wedged" state userspace may bring
interfaces down (to issue stop()) and then bring
one interface (to issue start()) to reload
hardware manually.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   30 ++++++++++
 drivers/net/wireless/ath/ath10k/core.h |   19 ++++++
 drivers/net/wireless/ath/ath10k/htc.c  |    3 +
 drivers/net/wireless/ath/ath10k/mac.c  |  101 ++++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/mac.h  |    1 +
 drivers/net/wireless/ath/ath10k/pci.c  |    2 +
 drivers/net/wireless/ath/ath10k/wmi.c  |    7 +++
 7 files changed, 144 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index c37f79f..7226c23 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar)
 	return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+	mutex_lock(&ar->conf_mutex);
+
+	switch (ar->state) {
+	case ATH10K_STATE_ON:
+		ath10k_halt(ar);
+		ar->state = ATH10K_STATE_RESTARTING;
+		ieee80211_restart_hw(ar->hw);
+		break;
+	case ATH10K_STATE_OFF:
+		/* this can happen if driver is being unloaded */
+		ath10k_warn("cannot restart a device that hasn't been started\n");
+		break;
+	case ATH10K_STATE_RESTARTING:
+	case ATH10K_STATE_RESTARTED:
+		ar->state = ATH10K_STATE_WEDGED;
+		/* fall through */
+	case ATH10K_STATE_WEDGED:
+		ath10k_warn("device is wedged, will not restart\n");
+		break;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops)
 {
@@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
 	init_waitqueue_head(&ar->event_queue);
 
+	INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
 	return ar;
 
 err_wq:
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 413f1c5..9f21ecb 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -250,6 +250,23 @@ struct ath10k_debug {
 enum ath10k_state {
 	ATH10K_STATE_OFF = 0,
 	ATH10K_STATE_ON,
+
+	/* When doing firmware recovery the device is first powered down.
+	 * mac80211 is supposed to call in to start() hook later on. It is
+	 * however possible that driver unloading and firmware crash overlap.
+	 * mac80211 can wait on conf_mutex in stop() while the device is
+	 * stopped in ath10k_core_restart() work holding conf_mutex. The state
+	 * RESTARTED means that the device is up and mac80211 has started hw
+	 * reconfiguration. Once mac80211 is done with the reconfiguration we
+	 * set the state to STATE_ON in restart_complete(). */
+	ATH10K_STATE_RESTARTING,
+	ATH10K_STATE_RESTARTED,
+
+	/* The device has crashed while restarting hw. This state is like ON
+	 * but commands are blocked in HTC and -ECOMM response is given. This
+	 * prevents completion timeouts and makes the driver more responsive to
+	 * userspace commands. This is also prevents recursive recovery. */
+	ATH10K_STATE_WEDGED,
 };
 
 struct ath10k {
@@ -355,6 +372,8 @@ struct ath10k {
 
 	enum ath10k_state state;
 
+	struct work_struct restart_work;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 7d5a366..72e072c 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
 	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+	if (htc->ar->state == ATH10K_STATE_WEDGED)
+		return -ECOMM;
+
 	if (eid >= ATH10K_HTC_EP_COUNT) {
 		ath10k_warn("Invalid endpoint id: %d\n", eid);
 		return -ENOENT;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 2dfd446..b1bb318 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1738,7 +1738,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
-static void ath10k_halt(struct ath10k *ar)
+void ath10k_halt(struct ath10k *ar)
 {
 	lockdep_assert_held(&ar->conf_mutex);
 
@@ -1764,7 +1764,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 
-	if (ar->state != ATH10K_STATE_OFF) {
+	if (ar->state != ATH10K_STATE_OFF &&
+	    ar->state != ATH10K_STATE_RESTARTING) {
 		ret = -EINVAL;
 		goto exit;
 	}
@@ -1784,6 +1785,11 @@ static int ath10k_start(struct ieee80211_hw *hw)
 		goto exit;
 	}
 
+	if (ar->state == ATH10K_STATE_OFF)
+		ar->state = ATH10K_STATE_ON;
+	else if (ar->state == ATH10K_STATE_RESTARTING)
+		ar->state = ATH10K_STATE_RESTARTED;
+
 	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
 	if (ret)
 		ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1806,22 +1812,47 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 	struct ath10k *ar = hw->priv;
 
 	mutex_lock(&ar->conf_mutex);
-	if (ar->state == ATH10K_STATE_ON)
+	if (ar->state == ATH10K_STATE_ON ||
+	    ar->state == ATH10K_STATE_RESTARTED ||
+	    ar->state == ATH10K_STATE_WEDGED)
 		ath10k_halt(ar);
 
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
+	cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
 	struct ath10k_generic_iter ar_iter;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	ieee80211_iterate_active_interfaces_atomic(
+		ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+		ath10k_ps_iter, &ar_iter);
+
+	if (ar_iter.ret)
+		ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
 	struct ath10k *ar = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
 	int ret = 0;
-	u32 flags;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -1833,18 +1864,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 		spin_unlock_bh(&ar->data_lock);
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_PS) {
-		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-		ar_iter.ar = ar;
-		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-		ieee80211_iterate_active_interfaces_atomic(hw,
-							   flags,
-							   ath10k_ps_iter,
-							   &ar_iter);
-
-		ret = ar_iter.ret;
-	}
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		ath10k_config_ps(ar);
 
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
 		if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1853,6 +1874,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 			ret = ath10k_monitor_destroy(ar);
 	}
 
+	ath10k_wmi_flush_tx(ar);
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
@@ -2695,6 +2717,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
 	rts = min_t(u32, rts, ATH10K_RTS_MAX);
 
 	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2735,6 +2764,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+		return;
+
 	frag = clamp_t(u32, frag,
 		       ATH10K_FRAGMT_THRESHOLD_MIN,
 		       ATH10K_FRAGMT_THRESHOLD_MAX);
@@ -2773,6 +2809,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
 	struct ath10k *ar = hw->priv;
+	bool skip;
 	int ret;
 
 	/* mac80211 doesn't care if we really xmit queued frames or not
@@ -2782,17 +2819,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 
 	mutex_lock(&ar->conf_mutex);
 
+	if (ar->state == ATH10K_STATE_WEDGED)
+		goto skip;
+
 	ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
 			bool empty;
+
 			spin_lock_bh(&ar->htt.tx_lock);
 			empty = bitmap_empty(ar->htt.used_msdu_ids,
 					     ar->htt.max_num_pending_tx);
 			spin_unlock_bh(&ar->htt.tx_lock);
-			(empty);
+
+			skip = (ar->state == ATH10K_STATE_WEDGED);
+
+			(empty || skip);
 		}), ATH10K_FLUSH_TIMEOUT_HZ);
-	if (ret <= 0)
+
+	if (ret <= 0 || skip)
 		ath10k_warn("tx not flushed\n");
 
+skip:
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -2866,6 +2912,22 @@ static int ath10k_resume(struct ieee80211_hw *hw)
 }
 #endif
 
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	mutex_lock(&ar->conf_mutex);
+
+	/* If device failed to restart it will be in a different state, e.g.
+	 * ATH10K_STATE_WEDGED */
+	if (ar->state == ATH10K_STATE_RESTARTED) {
+		ath10k_info("device successfully recovered\n");
+		ar->state = ATH10K_STATE_ON;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_tx,
 	.start				= ath10k_start,
@@ -2886,6 +2948,7 @@ static const struct ieee80211_ops ath10k_ops = {
 	.set_frag_threshold		= ath10k_set_frag_threshold,
 	.flush				= ath10k_flush,
 	.tx_last_beacon			= ath10k_tx_last_beacon,
+	.restart_complete		= ath10k_restart_complete,
 #ifdef CONFIG_PM
 	.suspend			= ath10k_suspend,
 	.resume				= ath10k_resume,
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 27fc92e..6fce9bf 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index bfe8561..c71b488 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
 			   reg_dump_values[i + 1],
 			   reg_dump_values[i + 2],
 			   reg_dump_values[i + 3]);
+
+	ieee80211_queue_work(ar->hw, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index b7e7e45..0d25cd7 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
 {
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (ar->state == ATH10K_STATE_WEDGED) {
+		ath10k_warn("wmi flush skipped - device is wedged anyway\n");
+		return;
+	}
+
 	ret = wait_event_timeout(ar->wmi.wq,
 				 atomic_read(&ar->wmi.pending_tx_count) == 0,
 				 5*HZ);
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 2/3] ath10k: implement fw crash simulation command
  2013-07-16  7:54     ` Michal Kazior
@ 2013-07-16  7:54       ` Michal Kazior
  -1 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful to test FW crash handling.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/wmi.c |   19 +++++++++++++++++++
 drivers/net/wireless/ath/ath10k/wmi.h |   19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 0d25cd7..5e42460 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2092,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
 	return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
 }
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+	struct wmi_force_fw_hang_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+	cmd->type = __cpu_to_le32(type);
+	cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+		   type, delay_ms);
+	return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 9555f5a..da3b2bc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -416,6 +416,7 @@ enum wmi_cmd_id {
 	WMI_PDEV_FTM_INTG_CMDID,
 	WMI_VDEV_SET_KEEPALIVE_CMDID,
 	WMI_VDEV_GET_KEEPALIVE_CMDID,
+	WMI_FORCE_FW_HANG_CMDID,
 
 	/* GPIO Configuration */
 	WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
 	struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+	WMI_FORCE_FW_HANG_ASSERT = 1,
+	WMI_FORCE_FW_HANG_NO_DETECT,
+	WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+	WMI_FORCE_FW_HANG_EMPTY_POINT,
+	WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+	WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+	__le32 type;
+	__le32 delay_ms;
+} __packed;
+
 #define ATH10K_RTS_MAX		2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN	540
 #define ATH10K_FRAGMT_THRESHOLD_MAX	2346
@@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 			const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms);
 
 #endif /* _WMI_H_ */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 2/3] ath10k: implement fw crash simulation command
@ 2013-07-16  7:54       ` Michal Kazior
  0 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful to test FW crash handling.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/wmi.c |   19 +++++++++++++++++++
 drivers/net/wireless/ath/ath10k/wmi.h |   19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 0d25cd7..5e42460 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2092,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
 	return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
 }
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+	struct wmi_force_fw_hang_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+	cmd->type = __cpu_to_le32(type);
+	cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+		   type, delay_ms);
+	return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 9555f5a..da3b2bc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -416,6 +416,7 @@ enum wmi_cmd_id {
 	WMI_PDEV_FTM_INTG_CMDID,
 	WMI_VDEV_SET_KEEPALIVE_CMDID,
 	WMI_VDEV_GET_KEEPALIVE_CMDID,
+	WMI_FORCE_FW_HANG_CMDID,
 
 	/* GPIO Configuration */
 	WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
 	struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+	WMI_FORCE_FW_HANG_ASSERT = 1,
+	WMI_FORCE_FW_HANG_NO_DETECT,
+	WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+	WMI_FORCE_FW_HANG_EMPTY_POINT,
+	WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+	WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+	__le32 type;
+	__le32 delay_ms;
+} __packed;
+
 #define ATH10K_RTS_MAX		2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN	540
 #define ATH10K_FRAGMT_THRESHOLD_MAX	2346
@@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 			const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms);
 
 #endif /* _WMI_H_ */
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
  2013-07-16  7:54     ` Michal Kazior
@ 2013-07-16  7:54       ` Michal Kazior
  -1 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful for testing. To perform a
forced firmware crash write 'crash' to
'simulate_fw_crash' debugfs file. E.g.

  echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 65279f5..4021bea 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -445,6 +445,57 @@ static const struct file_operations fops_fw_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTED) {
+		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return count;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -461,6 +512,9 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
 	return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
@ 2013-07-16  7:54       ` Michal Kazior
  0 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-16  7:54 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful for testing. To perform a
forced firmware crash write 'crash' to
'simulate_fw_crash' debugfs file. E.g.

  echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   54 +++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 65279f5..4021bea 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -445,6 +445,57 @@ static const struct file_operations fops_fw_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTED) {
+		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return count;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -461,6 +512,9 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
 	return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
  2013-07-16  7:54       ` Michal Kazior
@ 2013-07-19 10:29         ` Kalle Valo
  -1 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-19 10:29 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath10k, linux-wireless

Michal Kazior <michal.kazior@tieto.com> writes:

> This can be useful for testing. To perform a
> forced firmware crash write 'crash' to
> 'simulate_fw_crash' debugfs file. E.g.
>
>   echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

[...]

> +static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
> +					      const char __user *user_buf,
> +					      size_t count, loff_t *ppos)
> +{
> +	struct ath10k *ar = file->private_data;
> +	char buf[32] = {};
> +	int ret;
> +
> +	mutex_lock(&ar->conf_mutex);
> +
> +	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
> +	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
> +		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
> +		goto exit;
> +	}

Better to just return an error here.

> +	if (ar->state != ATH10K_STATE_ON &&
> +	    ar->state != ATH10K_STATE_RESTARTED) {
> +		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
> +		goto exit;
> +	}

Ditto.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
@ 2013-07-19 10:29         ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-19 10:29 UTC (permalink / raw)
  To: Michal Kazior; +Cc: linux-wireless, ath10k

Michal Kazior <michal.kazior@tieto.com> writes:

> This can be useful for testing. To perform a
> forced firmware crash write 'crash' to
> 'simulate_fw_crash' debugfs file. E.g.
>
>   echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

[...]

> +static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
> +					      const char __user *user_buf,
> +					      size_t count, loff_t *ppos)
> +{
> +	struct ath10k *ar = file->private_data;
> +	char buf[32] = {};
> +	int ret;
> +
> +	mutex_lock(&ar->conf_mutex);
> +
> +	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
> +	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
> +		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
> +		goto exit;
> +	}

Better to just return an error here.

> +	if (ar->state != ATH10K_STATE_ON &&
> +	    ar->state != ATH10K_STATE_RESTARTED) {
> +		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
> +		goto exit;
> +	}

Ditto.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] ath10k: hardware recovery
  2013-07-16  7:54     ` Michal Kazior
@ 2013-07-19 10:32       ` Kalle Valo
  -1 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-19 10:32 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath10k, linux-wireless

Michal Kazior <michal.kazior@tieto.com> writes:

> Respin of hw recovery. This implements recovery
> from a firmware crash.
>
> Note: this is based on my
>       "ath10k: device setup refactor" patchset.
>
> v3:
>  * rename RESTARTING_ON/OFF to RESTARTING/RESTARTED
>  * update commit message for 'create debufs..'
>
> Michal Kazior (3):
>   ath10k: implement device recovery
>   ath10k: implement fw crash simulation command
>   ath10k: create debugfs interface to trigger fw crash

Thanks, applied patches 1-2 and dropped patch 3.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] ath10k: hardware recovery
@ 2013-07-19 10:32       ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-19 10:32 UTC (permalink / raw)
  To: Michal Kazior; +Cc: linux-wireless, ath10k

Michal Kazior <michal.kazior@tieto.com> writes:

> Respin of hw recovery. This implements recovery
> from a firmware crash.
>
> Note: this is based on my
>       "ath10k: device setup refactor" patchset.
>
> v3:
>  * rename RESTARTING_ON/OFF to RESTARTING/RESTARTED
>  * update commit message for 'create debufs..'
>
> Michal Kazior (3):
>   ath10k: implement device recovery
>   ath10k: implement fw crash simulation command
>   ath10k: create debugfs interface to trigger fw crash

Thanks, applied patches 1-2 and dropped patch 3.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
  2013-07-19 10:29         ` Kalle Valo
@ 2013-07-20  6:01           ` Kalle Valo
  -1 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-20  6:01 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath10k, linux-wireless

Kalle Valo <kvalo@qca.qualcomm.com> writes:

>> +static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
>> +					      const char __user *user_buf,
>> +					      size_t count, loff_t *ppos)
>> +{
>> +	struct ath10k *ar = file->private_data;
>> +	char buf[32] = {};
>> +	int ret;
>> +
>> +	mutex_lock(&ar->conf_mutex);
>> +
>> +	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
>> +	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
>> +		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
>> +		goto exit;
>> +	}
>
> Better to just return an error here.
>
>> +	if (ar->state != ATH10K_STATE_ON &&
>> +	    ar->state != ATH10K_STATE_RESTARTED) {
>> +		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
>> +		goto exit;
>> +	}
>
> Ditto.

Sorry, I was unclear here. I also meant that the two ath10k_warn() calls
should be removed.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash
@ 2013-07-20  6:01           ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-20  6:01 UTC (permalink / raw)
  To: Michal Kazior; +Cc: linux-wireless, ath10k

Kalle Valo <kvalo@qca.qualcomm.com> writes:

>> +static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
>> +					      const char __user *user_buf,
>> +					      size_t count, loff_t *ppos)
>> +{
>> +	struct ath10k *ar = file->private_data;
>> +	char buf[32] = {};
>> +	int ret;
>> +
>> +	mutex_lock(&ar->conf_mutex);
>> +
>> +	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
>> +	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
>> +		ath10k_warn("write keyword `crash` to simulate firmware crash\n");
>> +		goto exit;
>> +	}
>
> Better to just return an error here.
>
>> +	if (ar->state != ATH10K_STATE_ON &&
>> +	    ar->state != ATH10K_STATE_RESTARTED) {
>> +		ath10k_warn("firmware isn't loaded yet, nothing to crash\n");
>> +		goto exit;
>> +	}
>
> Ditto.

Sorry, I was unclear here. I also meant that the two ath10k_warn() calls
should be removed.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v4] ath10k: create debugfs interface to trigger fw crash
  2013-07-20  6:01           ` Kalle Valo
@ 2013-07-22 12:08             ` Michal Kazior
  -1 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-22 12:08 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful for testing. To perform a
forced firmware crash write 'crash' to
'simulate_fw_crash' debugfs file. E.g.

  echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
v3: return an appropriate errno instead of using ath10k_warn()

 drivers/net/wireless/ath/ath10k/debug.c |   57 +++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 65279f5..3d65594 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -445,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTED) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+	if (ret == 0)
+		ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -461,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
 	return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v4] ath10k: create debugfs interface to trigger fw crash
@ 2013-07-22 12:08             ` Michal Kazior
  0 siblings, 0 replies; 26+ messages in thread
From: Michal Kazior @ 2013-07-22 12:08 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This can be useful for testing. To perform a
forced firmware crash write 'crash' to
'simulate_fw_crash' debugfs file. E.g.

  echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
v3: return an appropriate errno instead of using ath10k_warn()

 drivers/net/wireless/ath/ath10k/debug.c |   57 +++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 65279f5..3d65594 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -445,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTED) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+	if (ret == 0)
+		ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -461,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
 	return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v4] ath10k: create debugfs interface to trigger fw crash
  2013-07-22 12:08             ` Michal Kazior
@ 2013-07-23  8:02               ` Kalle Valo
  -1 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-23  8:02 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath10k, linux-wireless

Michal Kazior <michal.kazior@tieto.com> writes:

> This can be useful for testing. To perform a
> forced firmware crash write 'crash' to
> 'simulate_fw_crash' debugfs file. E.g.
>
>   echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
> ---
> v3: return an appropriate errno instead of using ath10k_warn()

Thanks, applied.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v4] ath10k: create debugfs interface to trigger fw crash
@ 2013-07-23  8:02               ` Kalle Valo
  0 siblings, 0 replies; 26+ messages in thread
From: Kalle Valo @ 2013-07-23  8:02 UTC (permalink / raw)
  To: Michal Kazior; +Cc: linux-wireless, ath10k

Michal Kazior <michal.kazior@tieto.com> writes:

> This can be useful for testing. To perform a
> forced firmware crash write 'crash' to
> 'simulate_fw_crash' debugfs file. E.g.
>
>   echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
> ---
> v3: return an appropriate errno instead of using ath10k_warn()

Thanks, applied.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2013-07-23  8:03 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <8c0a4f68cc3454b77a21ab949075fb0632549bb9>
2013-06-25  8:13 ` [ath9k-devel] [PATCH v2 0/3] ath10k: hardware recovery Michal Kazior
2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 1/3] ath10k: implement device recovery Michal Kazior
2013-07-05  7:47     ` Kalle Valo
2013-07-05  8:05       ` Michal Kazior
2013-07-15  7:19         ` Kalle Valo
2013-07-15  7:19           ` Kalle Valo
2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 2/3] ath10k: implement fw crash simulation command Michal Kazior
2013-06-25  8:13   ` [ath9k-devel] [PATCH v2 3/3] ath10k: create debugfs interface to trigger fw crash Michal Kazior
2013-07-16  7:54   ` [PATCH v3 0/3] ath10k: hardware recovery Michal Kazior
2013-07-16  7:54     ` Michal Kazior
2013-07-16  7:54     ` [PATCH v3 1/3] ath10k: implement device recovery Michal Kazior
2013-07-16  7:54       ` Michal Kazior
2013-07-16  7:54     ` [PATCH v3 2/3] ath10k: implement fw crash simulation command Michal Kazior
2013-07-16  7:54       ` Michal Kazior
2013-07-16  7:54     ` [PATCH v3 3/3] ath10k: create debugfs interface to trigger fw crash Michal Kazior
2013-07-16  7:54       ` Michal Kazior
2013-07-19 10:29       ` Kalle Valo
2013-07-19 10:29         ` Kalle Valo
2013-07-20  6:01         ` Kalle Valo
2013-07-20  6:01           ` Kalle Valo
2013-07-22 12:08           ` [PATCH v4] " Michal Kazior
2013-07-22 12:08             ` Michal Kazior
2013-07-23  8:02             ` Kalle Valo
2013-07-23  8:02               ` Kalle Valo
2013-07-19 10:32     ` [PATCH v3 0/3] ath10k: hardware recovery Kalle Valo
2013-07-19 10:32       ` Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.