]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wpa_supplicant: Complete radio works on disable event
authorAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Thu, 13 Feb 2014 09:24:00 +0000 (11:24 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 15 Feb 2014 22:06:23 +0000 (00:06 +0200)
While testing rfkill blocking of a scanning interface, it
was seen that the ongoing scan never completes. This happens
since EVENT_SCAN_RESULTS is discarded on a disabled interface.

Fix this and also other possible radio work completion issues
by removing all the radio works (including started) of the
disabled interface.

To be able to remove already started radio works, make their
callbacks be reentrant with deinit flag (when the work
is started), so each radio work should be able to handle
its own termination.

Signed-hostap: Andrei Otcheretianski <andrei.otcheretianski@intel.com>

wpa_supplicant/ctrl_iface.c
wpa_supplicant/events.c
wpa_supplicant/gas_query.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/scan.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 02292931d6b901d20df5f87afc4956971f87b67f..0d881333495da3acf0f775eacf73f0d833041f91 100644 (file)
@@ -5461,7 +5461,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
        eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
 
-       radio_remove_unstarted_work(wpa_s, NULL);
+       radio_remove_works(wpa_s, NULL, 1);
 }
 
 
@@ -5513,6 +5513,10 @@ static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
        struct wpa_external_work *ework = work->ctx;
 
        if (deinit) {
+               if (work->started)
+                       eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+                                            work, NULL);
+
                os_free(ework);
                return;
        }
index a72f2fae7e1af1ee02d7750952ed3ab35fea9d04..47434e4b3454824f08248ad0917a50599a7d9068 100644 (file)
@@ -3231,6 +3231,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_P2P */
 
                wpa_supplicant_mark_disassoc(wpa_s);
+               radio_remove_works(wpa_s, NULL, 0);
+
                wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
                break;
        case EVENT_CHANNEL_LIST_CHANGED:
index abcb391a14c9b5964eadec63b99152da034f6459..b2558470dfebc976d41fc3ec3849804ab755d477 100644 (file)
@@ -573,6 +573,12 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
        struct gas_query *gas = query->gas;
 
        if (deinit) {
+               if (work->started) {
+                       gas->work = NULL;
+                       gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+                       return;
+               }
+
                gas_query_free(query, 1);
                return;
        }
index 1495ea426d446f8ac3fb73a45ba652121e74c324..fa824914c934c37332b032be9ad3c59cec909792 100644 (file)
@@ -123,6 +123,7 @@ static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
                                        int group_added);
 static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_stop_listen(void *ctx);
 
 
 /*
@@ -252,7 +253,12 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
        int ret;
 
        if (deinit) {
-               wpa_scan_free_params(params);
+               if (!work->started) {
+                       wpa_scan_free_params(params);
+                       return;
+               }
+
+               wpa_s->p2p_scan_work = NULL;
                return;
        }
 
@@ -355,7 +361,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
                break;
        }
 
-       radio_remove_unstarted_work(wpa_s, "p2p-scan");
+       radio_remove_works(wpa_s, "p2p-scan", 0);
        if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
                           params) < 0)
                goto fail;
@@ -1001,6 +1007,12 @@ static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
        struct send_action_work *awork = work->ctx;
 
        if (deinit) {
+               if (work->started) {
+                       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                            wpa_s, NULL);
+                       wpa_s->p2p_send_action_work = NULL;
+                       offchannel_send_action_done(wpa_s);
+               }
                os_free(awork);
                return;
        }
@@ -1746,6 +1758,10 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
        struct wpas_p2p_listen_work *lwork = work->ctx;
 
        if (deinit) {
+               if (work->started) {
+                       wpa_s->p2p_listen_work = NULL;
+                       wpas_stop_listen(wpa_s);
+               }
                wpas_p2p_listen_work_free(lwork);
                return;
        }
index 18d243ebd40065857b2f65b6a3bfc996dc980f88..6c742d6d28bb5a6aa6f712d73ff7248722754b63 100644 (file)
@@ -148,7 +148,13 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
        int ret;
 
        if (deinit) {
-               wpa_scan_free_params(params);
+               if (!work->started) {
+                       wpa_scan_free_params(params);
+                       return;
+               }
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+               wpas_notify_scan_done(wpa_s, 0);
+               wpa_s->scan_work = NULL;
                return;
        }
 
index 451f5aebc24a4c7c2342932cd142be65b55a9a71..e712ac439e47ce7b6ad8564a1c1c6cd2aa5d118e 100644 (file)
@@ -444,6 +444,9 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
        struct wpa_supplicant *wpa_s = work->wpa_s;
 
        if (deinit) {
+               if (work->started)
+                       wpa_s->connect_work = NULL;
+
                wpas_connect_work_free(cwork);
                return;
        }
index e21c653a7411f37ce74b17ed044cec585efe8a84..e942b622588e19c4a0a68b0a199fa06e90d56da7 100644 (file)
@@ -1401,6 +1401,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 #endif /* CONFIG_HT_OVERRIDES */
 
        if (deinit) {
+               if (work->started) {
+                       wpa_s->connect_work = NULL;
+
+                       /* cancel possible auth. timeout */
+                       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+                                            NULL);
+               }
                wpas_connect_work_free(cwork);
                return;
        }
@@ -3077,25 +3084,40 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, const char *type)
+/*
+ * This function removes both started and pending radio works running on
+ * the provided interface's radio.
+ * Prior to the removal of the radio work, its callback (cb) is called with
+ * deinit set to be 1. Each work's callback is responsible for clearing its
+ * internal data and restoring to a correct state.
+ * @wpa_s: wpa_supplicant data
+ * @type: type of works to be removed
+ * @remove_all: 1 to remove all the works on this radio, 0 to remove only
+ * this interface's works.
+ */
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+                       const char *type, int remove_all)
 {
        struct wpa_radio_work *work, *tmp;
        struct wpa_radio *radio = wpa_s->radio;
 
        dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
                              list) {
-               if (type && (work->started || os_strcmp(type, work->type) != 0))
+               if (type && os_strcmp(type, work->type) != 0)
                        continue;
-               if (work->started) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Leaving started radio work '%s'@%p in the list",
-                               work->type, work);
+
+               /* skip other ifaces' works */
+               if (!remove_all && work->wpa_s != wpa_s)
                        continue;
-               }
-               wpa_dbg(wpa_s, MSG_DEBUG, "Remove unstarted radio work '%s'@%p",
-                       work->type, work);
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
+                       work->type, work, work->started ? " (started)" : "");
                work->cb(work, 1);
                radio_work_free(work);
        }
+
+       /* in case we removed the started work */
+       radio_work_check_next(wpa_s);
 }
 
 
@@ -3115,7 +3137,7 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s)
        }
 
        wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
-       radio_remove_unstarted_work(wpa_s, NULL);
+       radio_remove_works(wpa_s, NULL, 0);
        eloop_cancel_timeout(radio_start_next_work, radio, NULL);
        wpa_s->radio = NULL;
        os_free(radio);
index 4ca030689290dff347fa8a21965f36b086cd31f3..bcdb4d03696c4c5060ddc610f8b9215fcc4f66b0 100644 (file)
@@ -305,8 +305,8 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
                   void (*cb)(struct wpa_radio_work *work, int deinit),
                   void *ctx);
 void radio_work_done(struct wpa_radio_work *work);
-void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s,
-                                const char *type);
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+                       const char *type, int remove_all);
 void radio_work_check_next(struct wpa_supplicant *wpa_s);
 
 struct wpa_connect_work {