mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 13:02:44 +01:00
scan: Allow scan_cancel for finished requests
scan_request_failed and scan_finished remove the finished scan_request
from the request queue right away, before calling the callback. This
breaks those clients that rely on scan_cancel working on such requests
(i.e. to force the destroy callback to be invoked synchronously, see
a0911ca778
("station: Make sure roam_scan_id is always canceled").
Fix this by removing the scan_request from the request queue after
invoking the callback. Also provide a re-entrancy guard that will make
sure that the scan_request isn't removed in scan_cancel itself.
This commit is contained in:
parent
bef550df81
commit
62978ef0fb
54
src/scan.c
54
src/scan.c
@ -93,6 +93,7 @@ struct scan_request {
|
|||||||
* be false when the scan is complete and GET_SCAN is pending.
|
* be false when the scan is complete and GET_SCAN is pending.
|
||||||
*/
|
*/
|
||||||
bool triggered : 1;
|
bool triggered : 1;
|
||||||
|
bool in_callback : 1; /* Scan request complete, re-entrancy guard */
|
||||||
struct l_queue *cmds;
|
struct l_queue *cmds;
|
||||||
/* The time the current scan was started. Reported in TRIGGER_SCAN */
|
/* The time the current scan was started. Reported in TRIGGER_SCAN */
|
||||||
uint64_t start_time_tsf;
|
uint64_t start_time_tsf;
|
||||||
@ -164,13 +165,15 @@ static void scan_request_free(struct wiphy_radio_work_item *item)
|
|||||||
static void scan_request_failed(struct scan_context *sc,
|
static void scan_request_failed(struct scan_context *sc,
|
||||||
struct scan_request *sr, int err)
|
struct scan_request *sr, int err)
|
||||||
{
|
{
|
||||||
l_queue_remove(sc->requests, sr);
|
sr->in_callback = true;
|
||||||
|
|
||||||
if (sr->trigger)
|
if (sr->trigger)
|
||||||
sr->trigger(err, sr->userdata);
|
sr->trigger(err, sr->userdata);
|
||||||
else if (sr->callback)
|
else if (sr->callback)
|
||||||
sr->callback(err, NULL, NULL, sr->userdata);
|
sr->callback(err, NULL, NULL, sr->userdata);
|
||||||
|
|
||||||
|
sr->in_callback = false;
|
||||||
|
l_queue_remove(sc->requests, sr);
|
||||||
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,23 +818,30 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)
|
|||||||
if (!sr)
|
if (!sr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* We're in the callback and about to be removed, invoke destroy now */
|
||||||
|
if (sr->in_callback)
|
||||||
|
goto call_destroy;
|
||||||
|
|
||||||
/* If already triggered, just zero out the callback */
|
/* If already triggered, just zero out the callback */
|
||||||
if (sr == l_queue_peek_head(sc->requests) && sr->triggered) {
|
if (sr->triggered) {
|
||||||
l_debug("Scan is at the top of the queue and triggered");
|
l_debug("Scan has been triggered, wait for it to complete");
|
||||||
|
|
||||||
sr->callback = NULL;
|
sr->callback = NULL;
|
||||||
|
goto call_destroy;
|
||||||
if (sr->destroy) {
|
|
||||||
sr->destroy(sr->userdata);
|
|
||||||
sr->destroy = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we already sent the trigger command, cancel the scan */
|
/*
|
||||||
if (sr == l_queue_peek_head(sc->requests)) {
|
* Takes care of the following cases:
|
||||||
l_debug("Scan is at the top of the queue, but not triggered");
|
* 1. If TRIGGER_SCAN is in flight
|
||||||
|
* 2. TRIGGER_SCAN sent but bounced with -EBUSY
|
||||||
|
* 3. Scan request is done but GET_SCAN is still pending
|
||||||
|
*
|
||||||
|
* For case 3, we can easily cancel the command and proceed with the
|
||||||
|
* other pending requests. For case 1 & 2, the subsequent pending
|
||||||
|
* request might bounce off with an -EBUSY.
|
||||||
|
*/
|
||||||
|
if (wiphy_radio_work_is_running(sc->wiphy, sr->work.id)) {
|
||||||
|
l_debug("Scan is already started");
|
||||||
|
|
||||||
/* l_genl_family_cancel will trigger destroy callbacks */
|
/* l_genl_family_cancel will trigger destroy callbacks */
|
||||||
sr->canceled = true;
|
sr->canceled = true;
|
||||||
@ -843,12 +853,20 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)
|
|||||||
l_genl_family_cancel(nl80211, sc->get_scan_cmd_id);
|
l_genl_family_cancel(nl80211, sc->get_scan_cmd_id);
|
||||||
|
|
||||||
sc->start_cmd_id = 0;
|
sc->start_cmd_id = 0;
|
||||||
l_queue_remove(sc->requests, sr);
|
sc->get_scan_cmd_id = 0;
|
||||||
} else
|
}
|
||||||
l_queue_remove(sc->requests, sr);
|
|
||||||
|
|
||||||
|
l_queue_remove(sc->requests, sr);
|
||||||
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
call_destroy:
|
||||||
|
if (sr->destroy) {
|
||||||
|
sr->destroy(sr->userdata);
|
||||||
|
sr->destroy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1710,7 +1728,7 @@ static void scan_finished(struct scan_context *sc,
|
|||||||
discover_hidden_network_bsses(sc, bss_list);
|
discover_hidden_network_bsses(sc, bss_list);
|
||||||
|
|
||||||
if (sr)
|
if (sr)
|
||||||
l_queue_remove(sc->requests, sr);
|
sr->in_callback = true;
|
||||||
|
|
||||||
if (callback)
|
if (callback)
|
||||||
new_owner = callback(err, bss_list, freqs, userdata);
|
new_owner = callback(err, bss_list, freqs, userdata);
|
||||||
@ -1728,6 +1746,8 @@ static void scan_finished(struct scan_context *sc,
|
|||||||
* SCAN_FINISHED or SCAN_ABORTED handler would have taken care of
|
* SCAN_FINISHED or SCAN_ABORTED handler would have taken care of
|
||||||
* sending the next command for a new or ongoing scan.
|
* sending the next command for a new or ongoing scan.
|
||||||
*/
|
*/
|
||||||
|
sr->in_callback = false;
|
||||||
|
l_queue_remove(sc->requests, sr);
|
||||||
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user