staging: wfx: avoid possible lock-up during scan
If the environment is noisy, the device may take time to send scan requests. Thus, scan requests durations > 5s have already been observed. During the scan, traffic is neither received, neither sent. From the user point-of-view, the traffic is frozen for a long time. This patch reworks the scan processing. It gives to the device a smaller time budget than previously. However, it does not expect the scan to be complete and it is able to send another scan request to finish the work. A big part of the patch aims to avoid an infinite loop if the device goes crazy. Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com> Link: https://lore.kernel.org/r/20210913130203.1903622-6-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
10b72a7c59
commit
8bce06b06b
|
@ -175,13 +175,14 @@ static int hif_scan_complete_indication(struct wfx_dev *wdev,
|
|||
const void *buf)
|
||||
{
|
||||
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
|
||||
const struct hif_ind_scan_cmpl *body = buf;
|
||||
|
||||
if (!wvif) {
|
||||
dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
wfx_scan_complete(wvif);
|
||||
wfx_scan_complete(wvif, body->num_channels_completed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ static int update_probe_tmpl(struct wfx_vif *wvif,
|
|||
static int send_scan_req(struct wfx_vif *wvif,
|
||||
struct cfg80211_scan_request *req, int start_idx)
|
||||
{
|
||||
int i, ret, timeout;
|
||||
int i, ret;
|
||||
struct ieee80211_channel *ch_start, *ch_cur;
|
||||
|
||||
for (i = start_idx; i < req->n_channels; i++) {
|
||||
|
@ -56,31 +56,31 @@ static int send_scan_req(struct wfx_vif *wvif,
|
|||
wfx_tx_lock_flush(wvif->wdev);
|
||||
wvif->scan_abort = false;
|
||||
reinit_completion(&wvif->scan_complete);
|
||||
ret = hif_scan(wvif, req, start_idx, i - start_idx, &timeout);
|
||||
ret = hif_scan(wvif, req, start_idx, i - start_idx, NULL);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto err_scan_start;
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
return -EIO;
|
||||
}
|
||||
ret = wait_for_completion_timeout(&wvif->scan_complete, timeout);
|
||||
ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
|
||||
if (!ret) {
|
||||
dev_notice(wvif->wdev->dev, "scan timeout\n");
|
||||
hif_stop_scan(wvif);
|
||||
ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
|
||||
if (!ret)
|
||||
dev_err(wvif->wdev->dev, "scan didn't stop\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_timeout;
|
||||
dev_dbg(wvif->wdev->dev, "scan timeout (%d channels done)\n",
|
||||
wvif->scan_nb_chan_done);
|
||||
}
|
||||
if (wvif->scan_abort) {
|
||||
if (!ret) {
|
||||
dev_err(wvif->wdev->dev, "scan didn't stop\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else if (wvif->scan_abort) {
|
||||
dev_notice(wvif->wdev->dev, "scan abort\n");
|
||||
ret = -ECONNABORTED;
|
||||
goto err_timeout;
|
||||
} else if (wvif->scan_nb_chan_done > i - start_idx) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = wvif->scan_nb_chan_done;
|
||||
}
|
||||
ret = i - start_idx;
|
||||
err_timeout:
|
||||
if (req->channels[start_idx]->max_power != wvif->vif->bss_conf.txpower)
|
||||
hif_set_output_power(wvif, wvif->vif->bss_conf.txpower);
|
||||
err_scan_start:
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
return ret;
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ void wfx_hw_scan_work(struct work_struct *work)
|
|||
{
|
||||
struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
|
||||
struct ieee80211_scan_request *hw_req = wvif->scan_req;
|
||||
int chan_cur, ret;
|
||||
int chan_cur, ret, err;
|
||||
|
||||
mutex_lock(&wvif->wdev->conf_mutex);
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
|
@ -105,11 +105,20 @@ void wfx_hw_scan_work(struct work_struct *work)
|
|||
}
|
||||
update_probe_tmpl(wvif, &hw_req->req);
|
||||
chan_cur = 0;
|
||||
err = 0;
|
||||
do {
|
||||
ret = send_scan_req(wvif, &hw_req->req, chan_cur);
|
||||
if (ret > 0)
|
||||
if (ret > 0) {
|
||||
chan_cur += ret;
|
||||
} while (ret > 0 && chan_cur < hw_req->req.n_channels);
|
||||
err = 0;
|
||||
}
|
||||
if (!ret)
|
||||
err++;
|
||||
if (err > 2) {
|
||||
dev_err(wvif->wdev->dev, "scan has not been able to start\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
} while (ret >= 0 && chan_cur < hw_req->req.n_channels);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
__ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
|
||||
|
@ -134,7 +143,8 @@ void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|||
hif_stop_scan(wvif);
|
||||
}
|
||||
|
||||
void wfx_scan_complete(struct wfx_vif *wvif)
|
||||
void wfx_scan_complete(struct wfx_vif *wvif, int nb_chan_done)
|
||||
{
|
||||
wvif->scan_nb_chan_done = nb_chan_done;
|
||||
complete(&wvif->scan_complete);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ void wfx_hw_scan_work(struct work_struct *work);
|
|||
int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *req);
|
||||
void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
void wfx_scan_complete(struct wfx_vif *wvif);
|
||||
void wfx_scan_complete(struct wfx_vif *wvif, int nb_chan_done);
|
||||
|
||||
#endif /* WFX_SCAN_H */
|
||||
|
|
|
@ -85,6 +85,7 @@ struct wfx_vif {
|
|||
struct mutex scan_lock;
|
||||
struct work_struct scan_work;
|
||||
struct completion scan_complete;
|
||||
int scan_nb_chan_done;
|
||||
bool scan_abort;
|
||||
struct ieee80211_scan_request *scan_req;
|
||||
|
||||
|
|
Loading…
Reference in New Issue