wil6210: manual FW error recovery mode
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Wed, 1 Oct 2014 12:05:24 +0000 (15:05 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 2 Oct 2014 18:23:14 +0000 (14:23 -0400)
Introduce manual FW recovery mode. It is activated if module parameter
@no_fw_recovery set to true. May be changed at runtime.

Recovery information provided by new "recovery" debugfs file. It prints:

mode = [auto|manual]
state = [idle|pending|running]

In manual mode, after FW error, recovery won't start automatically. Instead,
after notification to user space, recovery waits in "pending" state, as indicated by the
"recovery" debugfs file. User space tools may perform data collection and allow to
continue recovery by writing "run" to the "recovery" debugfs file.
Alternatively, recovery pending may be canceled by stopping network interface
i.e. 'ifconfig wlan0 down'

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c

index f3a31e8c253541591529bd77bae60f9f2393dae3..d9f4b30dd343b01e1d58ac8c9deadbb246a82abd 100644 (file)
@@ -728,6 +728,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                wil_print_bcon_data(bcon);
        }
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
+
        mutex_lock(&wil->mutex);
 
        __wil_down(wil);
@@ -775,6 +777,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
+
        mutex_lock(&wil->mutex);
 
        rc = wmi_pcp_stop(wil);
index eb2204e5fdd4f8cb7044579aaa99b4de5e508d48..54a6ddc6301bcabe3c48784e850033c6a170410e 100644 (file)
@@ -1041,6 +1041,71 @@ static const struct file_operations fops_info = {
        .llseek         = seq_lseek,
 };
 
+/*---------recovery------------*/
+/* mode = [manual|auto]
+ * state = [idle|pending|running]
+ */
+static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       char buf[80];
+       int n;
+       static const char * const sstate[] = {"idle", "pending", "running"};
+
+       n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
+                    no_fw_recovery ? "manual" : "auto",
+                    sstate[wil->recovery_state]);
+
+       n = min_t(int, n, sizeof(buf));
+
+       return simple_read_from_buffer(user_buf, count, ppos,
+                                      buf, n);
+}
+
+static ssize_t wil_write_file_recovery(struct file *file,
+                                      const char __user *buf_,
+                                      size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       static const char run_command[] = "run";
+       char buf[sizeof(run_command) + 1]; /* to detect "runx" */
+       ssize_t rc;
+
+       if (wil->recovery_state != fw_recovery_pending) {
+               wil_err(wil, "No recovery pending\n");
+               return -EINVAL;
+       }
+
+       if (*ppos != 0) {
+               wil_err(wil, "Offset [%d]\n", (int)*ppos);
+               return -EINVAL;
+       }
+
+       if (count > sizeof(buf)) {
+               wil_err(wil, "Input too long, len = %d\n", (int)count);
+               return -EINVAL;
+       }
+
+       rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
+       if (rc < 0)
+               return rc;
+
+       buf[rc] = '\0';
+       if (0 == strcmp(buf, run_command))
+               wil_set_recovery_state(wil, fw_recovery_running);
+       else
+               wil_err(wil, "Bad recovery command \"%s\"\n", buf);
+
+       return rc;
+}
+
+static const struct file_operations fops_recovery = {
+       .read = wil_read_file_recovery,
+       .write = wil_write_file_recovery,
+       .open  = simple_open,
+};
+
 /*---------Station matrix------------*/
 static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
 {
@@ -1152,6 +1217,7 @@ static const struct {
        {"freq",        S_IRUGO,                &fops_freq},
        {"link",        S_IRUGO,                &fops_link},
        {"info",        S_IRUGO,                &fops_info},
+       {"recovery",    S_IRUGO | S_IWUSR,      &fops_recovery},
 };
 
 static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
@@ -1194,6 +1260,7 @@ static const struct dbg_off dbg_wil_off[] = {
        WIL_FIELD(status,       S_IRUGO | S_IWUSR,      doff_ulong),
        WIL_FIELD(fw_version,   S_IRUGO,                doff_u32),
        WIL_FIELD(hw_version,   S_IRUGO,                doff_x32),
+       WIL_FIELD(recovery_count, S_IRUGO,              doff_u32),
        {},
 };
 
index 0424763fd5a1c22cd8d376933ad445ed8447fc2f..6500caf8d609ccd90fae94ce704635df1b50cb16 100644 (file)
@@ -25,9 +25,9 @@
 #define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
 #define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
 
-static bool no_fw_recovery;
+bool no_fw_recovery;
 module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
+MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
 
 static bool no_fw_load = true;
 module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
@@ -191,17 +191,38 @@ static void wil_scan_timer_fn(ulong x)
        schedule_work(&wil->fw_error_worker);
 }
 
+static int wil_wait_for_recovery(struct wil6210_priv *wil)
+{
+       if (wait_event_interruptible(wil->wq, wil->recovery_state !=
+                                    fw_recovery_pending)) {
+               wil_err(wil, "Interrupt, canceling recovery\n");
+               return -ERESTARTSYS;
+       }
+       if (wil->recovery_state != fw_recovery_running) {
+               wil_info(wil, "Recovery cancelled\n");
+               return -EINTR;
+       }
+       wil_info(wil, "Proceed with recovery\n");
+       return 0;
+}
+
+void wil_set_recovery_state(struct wil6210_priv *wil, int state)
+{
+       wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__,
+                    wil->recovery_state, state);
+
+       wil->recovery_state = state;
+       wake_up_interruptible(&wil->wq);
+}
+
 static void wil_fw_error_worker(struct work_struct *work)
 {
-       struct wil6210_priv *wil = container_of(work,
-                       struct wil6210_priv, fw_error_worker);
+       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+                                               fw_error_worker);
        struct wireless_dev *wdev = wil->wdev;
 
        wil_dbg_misc(wil, "fw error worker\n");
 
-       if (no_fw_recovery)
-               return;
-
        /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
         * passed since last recovery attempt
         */
@@ -224,8 +245,13 @@ static void wil_fw_error_worker(struct work_struct *work)
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_MONITOR:
-               wil_info(wil, "fw error recovery started (try %d)...\n",
+               wil_info(wil, "fw error recovery requested (try %d)...\n",
                         wil->recovery_count);
+               if (!no_fw_recovery)
+                       wil->recovery_state = fw_recovery_running;
+               if (0 != wil_wait_for_recovery(wil))
+                       break;
+
                __wil_down(wil);
                __wil_up(wil);
                break;
@@ -302,6 +328,7 @@ int wil_priv_init(struct wil6210_priv *wil)
 
        INIT_LIST_HEAD(&wil->pending_wmi_ev);
        spin_lock_init(&wil->wmi_ev_lock);
+       init_waitqueue_head(&wil->wq);
 
        wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
        if (!wil->wmi_wq)
@@ -331,6 +358,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
 {
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        del_timer_sync(&wil->scan_timer);
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
@@ -573,6 +601,7 @@ int wil_reset(struct wil6210_priv *wil)
 void wil_fw_error_recovery(struct wil6210_priv *wil)
 {
        wil_dbg_misc(wil, "starting fw error recovery\n");
+       wil->recovery_state = fw_recovery_pending;
        schedule_work(&wil->fw_error_worker);
 }
 
@@ -724,6 +753,7 @@ int wil_down(struct wil6210_priv *wil)
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        mutex_lock(&wil->mutex);
        rc = __wil_down(wil);
        mutex_unlock(&wil->mutex);
index 00c4f0a71fb8453a0c0530705ad3d46275a925e2..2991609885f7971b4f206915f27d2b70e5bb3635 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/timex.h>
 #include "wil_platform.h"
 
+extern bool no_fw_recovery;
 
 #define WIL_NAME "wil6210"
 #define WIL_FW_NAME "wil6210.fw"
@@ -379,6 +380,12 @@ struct wil_sta_info {
        unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
 };
 
+enum {
+       fw_recovery_idle = 0,
+       fw_recovery_pending = 1,
+       fw_recovery_running = 2,
+};
+
 struct wil6210_priv {
        struct pci_dev *pdev;
        int n_msi;
@@ -389,8 +396,10 @@ struct wil6210_priv {
        u32 hw_version;
        struct wil_board *board;
        u8 n_mids; /* number of additional MIDs as reported by FW */
-       int recovery_count; /* num of FW recovery attempts in a short time */
+       u32 recovery_count; /* num of FW recovery attempts in a short time */
+       u32 recovery_state; /* FW recovery state machine */
        unsigned long last_fw_recovery; /* jiffies of last fw recovery */
+       wait_queue_head_t wq; /* for all wait_event() use */
        /* profile */
        u32 monitor_flags;
        u32 secure_pcp; /* create secure PCP? */
@@ -507,6 +516,7 @@ void wil_priv_deinit(struct wil6210_priv *wil);
 int wil_reset(struct wil6210_priv *wil);
 void wil_set_itr_trsh(struct wil6210_priv *wil);
 void wil_fw_error_recovery(struct wil6210_priv *wil);
+void wil_set_recovery_state(struct wil6210_priv *wil, int state);
 void wil_link_on(struct wil6210_priv *wil);
 void wil_link_off(struct wil6210_priv *wil);
 int wil_up(struct wil6210_priv *wil);
index bd781c7adf2a5f40b513b2ade95e732bed1e7930..4311df982c6074cf54e2fe43a8971f4aa355725e 100644 (file)
@@ -299,6 +299,7 @@ static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
 {
        wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        set_bit(wil_status_fwready, &wil->status);
        /* let the reset sequence continue */
        complete(&wil->wmi_ready);