[PATCH v7] netdev: use wiphy radio work queue for connections
by James Prestwood
This adds connection/FT attempts to the radio work queue. This
will ensure that connections aren't delayed or done concurrently
with scanning.
---
src/netdev.c | 190 +++++++++++++++++++++++++++++++++++---------------
src/station.c | 8 +--
2 files changed, 138 insertions(+), 60 deletions(-)
v7:
- Unref/NULL netdev->connect_cmd in one place
- Rework some logic in netdev_being_connection
diff --git a/src/netdev.c b/src/netdev.c
index e469fb1c..9bfbe03c 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -136,6 +136,9 @@ struct netdev {
struct l_io *pae_io; /* for drivers without EAPoL over NL80211 */
+ struct l_genl_msg *connect_cmd;
+ struct wiphy_radio_work_item work;
+
bool connected : 1;
bool operational : 1;
bool rekey_offload_support : 1;
@@ -485,6 +488,9 @@ static void netdev_preauth_destroy(void *data)
static void netdev_connect_free(struct netdev *netdev)
{
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
+
if (netdev->sm) {
eapol_sm_free(netdev->sm);
netdev->sm = NULL;
@@ -530,6 +536,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->ignore_connect_event = false;
netdev->expect_connect_failure = false;
+ if (netdev->connect_cmd) {
+ l_genl_msg_unref(netdev->connect_cmd);
+ netdev->connect_cmd = NULL;
+ }
+
netdev_rssi_polling_update(netdev);
if (netdev->connect_cmd_id) {
@@ -1019,6 +1030,9 @@ static void netdev_connect_ok(struct netdev *netdev)
}
netdev_rssi_polling_update(netdev);
+
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
}
static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
@@ -1487,6 +1501,9 @@ void netdev_handshake_failed(struct handshake_state *hs, uint16_t reason_code)
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending DEL_STATION");
}
+
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
}
static void hardware_rekey_cb(struct l_genl_msg *msg, void *data)
@@ -2421,30 +2438,30 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct rtnl_data {
struct netdev *netdev;
- struct l_genl_msg *cmd_connect;
uint8_t addr[ETH_ALEN];
int ref;
};
-static int netdev_begin_connection(struct netdev *netdev,
- struct l_genl_msg *cmd_connect)
+static int netdev_begin_connection(struct netdev *netdev)
{
- if (cmd_connect) {
- netdev->connect_cmd_id = l_genl_family_send(nl80211,
- cmd_connect, netdev_cmd_connect_cb,
- netdev, NULL);
+ netdev->connect_cmd_id = l_genl_family_send(nl80211,
+ netdev->connect_cmd,
+ netdev_cmd_connect_cb, netdev, NULL);
- if (!netdev->connect_cmd_id) {
- l_genl_msg_unref(cmd_connect);
- return -EIO;
- }
+ if (!netdev->connect_cmd_id) {
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+ return -EIO;
}
+ /*
+ * Set the supplicant address now, this may have already been done for
+ * a non-randomized address connect, but if we are randomizing we need
+ * to set it again as the address should have now changed.
+ */
handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
- /* set connected since the auth protocols cannot do so internally */
- if (netdev->ap && auth_proto_start(netdev->ap))
- netdev->connected = true;
+ netdev->connect_cmd = NULL;
return 0;
}
@@ -2461,8 +2478,6 @@ static void netdev_mac_change_failed(struct netdev *netdev,
* mac_change_cmd_id was set.
*/
if (!netdev_get_is_up(netdev)) {
- l_genl_msg_unref(req->cmd_connect);
-
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
netdev, NETDEV_WATCH_EVENT_DOWN);
@@ -2474,7 +2489,7 @@ static void netdev_mac_change_failed(struct netdev *netdev,
*/
l_info("Interface still up after failing to change the MAC, "
"continuing with connection");
- if (netdev_begin_connection(netdev, req->cmd_connect) < 0)
+ if (netdev_begin_connection(netdev) < 0)
goto failed;
return;
@@ -2517,7 +2532,7 @@ static void netdev_mac_power_up_cb(int error, uint16_t type,
/*
* Pick up where we left off in netdev_connect_commmon.
*/
- if (netdev_begin_connection(netdev, req->cmd_connect) < 0) {
+ if (netdev_begin_connection(netdev) < 0) {
l_error("Failed to connect after changing MAC");
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
@@ -2576,9 +2591,7 @@ static void netdev_mac_power_down_cb(int error, uint16_t type,
* Returns -EALREADY if the requested MAC matched our current MAC
* Returns -EIO if there was an IO error when powering down
*/
-static int netdev_start_powered_mac_change(struct netdev *netdev,
- struct scan_bss *bss,
- struct l_genl_msg *cmd_connect)
+static int netdev_start_powered_mac_change(struct netdev *netdev)
{
struct rtnl_data *req;
uint8_t new_addr[6];
@@ -2586,8 +2599,8 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
/* No address set in handshake, use per-network MAC generation */
if (util_mem_is_zero(netdev->handshake->spa, ETH_ALEN))
wiphy_generate_address_from_ssid(netdev->wiphy,
- (const char *)bss->ssid,
- new_addr);
+ (const char *)netdev->handshake->ssid,
+ new_addr);
else
memcpy(new_addr, netdev->handshake->spa, ETH_ALEN);
@@ -2600,7 +2613,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
req = l_new(struct rtnl_data, 1);
req->netdev = netdev;
/* This message will need to be unreffed upon any error */
- req->cmd_connect = cmd_connect;
req->ref++;
memcpy(req->addr, new_addr, sizeof(req->addr));
@@ -2609,7 +2621,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
req, netdev_mac_destroy);
if (!netdev->mac_change_cmd_id) {
- l_genl_msg_unref(req->cmd_connect);
l_free(req);
return -EIO;
@@ -2618,6 +2629,66 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
return 0;
}
+static bool netdev_connection_work_ready(struct wiphy_radio_work_item *item)
+{
+ struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+ if (mac_per_ssid) {
+ int ret = netdev_start_powered_mac_change(netdev);
+
+ if (!ret)
+ return false;
+ else if (ret != -EALREADY)
+ goto failed;
+ }
+
+ if (netdev_begin_connection(netdev) < 0)
+ goto failed;
+
+ return false;
+
+failed:
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+
+ return true;
+}
+
+static const struct wiphy_radio_work_item_ops connect_work_ops = {
+ .do_work = netdev_connection_work_ready,
+};
+
+static bool netdev_ap_work_ready(struct wiphy_radio_work_item *item)
+{
+ struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+ handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+
+ if (!auth_proto_start(netdev->ap)) {
+ /* Restore original nonce if this was an FT attempt */
+ if (netdev->in_ft) {
+ memcpy(netdev->handshake->snonce,
+ netdev->prev_snonce, 32);
+
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+ return true;
+ }
+ }
+
+ /*
+ * set connected since the auth protocols cannot do
+ * so internally
+ */
+ netdev->connected = true;
+
+ return false;
+}
+
+static const struct wiphy_radio_work_item_ops ap_work_ops = {
+ .do_work = netdev_ap_work_ready,
+};
+
static int netdev_connect_common(struct netdev *netdev,
struct l_genl_msg *cmd_connect,
struct scan_bss *bss,
@@ -2626,6 +2697,7 @@ static int netdev_connect_common(struct netdev *netdev,
netdev_event_func_t event_filter,
netdev_connect_cb_t cb, void *user_data)
{
+ netdev->connect_cmd = cmd_connect;
netdev->event_filter = event_filter;
netdev->connect_cb = cb;
netdev->user_data = user_data;
@@ -2642,14 +2714,9 @@ static int netdev_connect_common(struct netdev *netdev,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
handshake_state_set_no_rekey(hs, true);
- if (mac_per_ssid) {
- int ret = netdev_start_powered_mac_change(netdev, bss,
- cmd_connect);
- if (ret != -EALREADY)
- return ret;
- }
-
- return netdev_begin_connection(netdev, cmd_connect);
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+ netdev->ap ? &ap_work_ops : &connect_work_ops);
+ return 0;
}
int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
@@ -2667,7 +2734,7 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
return -ENOTSUP;
- if (netdev->connected || netdev->connect_cmd_id)
+ if (netdev->connected || netdev->connect_cmd_id || netdev->work.id)
return -EISCONN;
switch (hs->akm_suite) {
@@ -2709,23 +2776,36 @@ int netdev_disconnect(struct netdev *netdev,
netdev_disconnect_cb_t cb, void *user_data)
{
struct l_genl_msg *disconnect;
+ bool send_disconnect = true;
if (netdev->type != NL80211_IFTYPE_STATION &&
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
return -ENOTSUP;
- if (!netdev->connected)
- return -ENOTCONN;
-
if (netdev->disconnect_cmd_id)
return -EINPROGRESS;
/* Only perform this if we haven't successfully fully associated yet */
if (!netdev->operational) {
+ /*
+ * Three possibilities here:
+ * 1. We do not actually have a connect in progress (work.id
+ * is zero), then we can bail out early with an error.
+ * 2. We have sent CMD_CONNECT but not fully connected. The
+ * CMD_CONNECT needs to be canceled and a disconnect should
+ * be sent.
+ * 3. Queued up the connect work, but haven't sent CMD_CONNECT
+ * to the kernel. This case we do not need to send a
+ * disconnect.
+ */
+ if (!netdev->work.id)
+ return -ENOTCONN;
+
if (netdev->connect_cmd_id) {
l_genl_family_cancel(nl80211, netdev->connect_cmd_id);
netdev->connect_cmd_id = 0;
- }
+ } else
+ send_disconnect = false;
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
MMPDU_REASON_CODE_UNSPECIFIED);
@@ -2733,19 +2813,23 @@ int netdev_disconnect(struct netdev *netdev,
netdev_connect_free(netdev);
}
- disconnect = netdev_build_cmd_disconnect(netdev,
+ if (send_disconnect) {
+ disconnect = netdev_build_cmd_disconnect(netdev,
MMPDU_REASON_CODE_DEAUTH_LEAVING);
- netdev->disconnect_cmd_id = l_genl_family_send(nl80211, disconnect,
- netdev_cmd_disconnect_cb, netdev, NULL);
+ netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
+ disconnect, netdev_cmd_disconnect_cb,
+ netdev, NULL);
- if (!netdev->disconnect_cmd_id) {
- l_genl_msg_unref(disconnect);
- return -EIO;
- }
+ if (!netdev->disconnect_cmd_id) {
+ l_genl_msg_unref(disconnect);
+ return -EIO;
+ }
- netdev->disconnect_cb = cb;
- netdev->user_data = user_data;
- netdev->aborting = true;
+ netdev->disconnect_cb = cb;
+ netdev->user_data = user_data;
+ netdev->aborting = true;
+ } else if (cb)
+ cb(netdev, true, user_data);
return 0;
}
@@ -3092,7 +3176,6 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_connect_cb_t cb)
{
struct netdev_handshake_state *nhs;
- int err = -EINVAL;
if (!netdev->operational)
return -ENOTCONN;
@@ -3173,15 +3256,10 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_ft_over_ds_tx_authenticate,
netdev_ft_tx_associate, netdev);
- if (!auth_proto_start(netdev->ap))
- goto restore_snonce;
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+ &ap_work_ops);
return 0;
-
-restore_snonce:
- memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
- return err;
}
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
diff --git a/src/station.c b/src/station.c
index 35f4a96d..0095d6c8 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2702,10 +2702,6 @@ int station_disconnect(struct station *station)
if (!station->connected_bss)
return -ENOTCONN;
- if (netdev_disconnect(station->netdev,
- station_disconnect_cb, station) < 0)
- return -EIO;
-
if (station->netconfig)
netconfig_reset(station->netconfig);
@@ -2718,6 +2714,10 @@ int station_disconnect(struct station *station)
station_enter_state(station, STATION_STATE_DISCONNECTING);
+ if (netdev_disconnect(station->netdev,
+ station_disconnect_cb, station) < 0)
+ return -EIO;
+
return 0;
}
--
2.21.1
1 year, 11 months
[PATCH 1/2] auto-t: fix hostapd.ungraceful_restart
by James Prestwood
Restarting hostapd from python was actually leaking memory and
causing the hostapd object to stay referenced in python. The
GLib timeout in wait_for_event was the ultimate cause, but this
had no come to light because no tests restarted hostapd then
used wait_for_event.
In addition, any use of wait_for_event after a restart would
cause an exception because the event socket was never re-attached
after hostapd restarted.
Now we properly clean up the timeout in wait_for_event and
re-initialize the hostapd object on restart.
---
autotests/util/hostapd.py | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/autotests/util/hostapd.py b/autotests/util/hostapd.py
index 49047f38..0ed4e004 100644
--- a/autotests/util/hostapd.py
+++ b/autotests/util/hostapd.py
@@ -33,7 +33,7 @@ hostapd_map = {ifname: intf for wname, wiphy in wiphy_map.items()
if wiphy.use == 'hostapd'}
class HostapdCLI:
- def __init__(self, interface=None, config=None):
+ def _init_hostapd(self, interface=None, config=None):
global ctrl_count
if not interface and not config:
@@ -54,7 +54,8 @@ class HostapdCLI:
self.cmdline = 'hostapd_cli -p"' + self.socket_path + '" -i"' + \
self.ifname + '"'
- self._hostapd_restarted = False
+ if not hasattr(self, '_hostapd_restarted'):
+ self._hostapd_restarted = False
self.local_ctrl = '/tmp/hostapd_' + str(os.getpid()) + '_' + \
str(ctrl_count)
@@ -67,6 +68,9 @@ class HostapdCLI:
ctrl_count = ctrl_count + 1
+ def __init__(self, interface=None, config=None):
+ self._init_hostapd(interface, config)
+
def wait_for_event(self, event, timeout=10):
global mainloop
self._wait_timed_out = False
@@ -84,6 +88,7 @@ class HostapdCLI:
while self._data_available(0.25):
data = self.ctrl_sock.recv(4096).decode('utf-8')
if event in data:
+ GLib.source_remove(timeout)
return data
if self._wait_timed_out:
@@ -108,11 +113,20 @@ class HostapdCLI:
raise Exception('timeout waiting for control response')
- def __del__(self):
+ def _del_hostapd(self, force=False):
+ self.ctrl_sock.close()
+
if self._hostapd_restarted:
- os.system('killall hostapd')
+ if force:
+ os.system('killall -9 hostapd')
+ else:
+ os.system('killall hostapd')
- self.ctrl_sock.close()
+ os.system('ifconfig %s down' % self.ifname)
+ os.system('ifconfig %s up' % self.ifname)
+
+ def __del__(self):
+ self._del_hostapd()
def wps_push_button(self):
os.system(self.cmdline + ' wps_pbc')
@@ -193,15 +207,20 @@ class HostapdCLI:
'''
Ungracefully kill and restart hostapd
'''
+ # set flag so hostapd can be killed after the test
+ self._hostapd_restarted = True
intf = hostapd_map[self.ifname]
- os.system('killall -9 hostapd')
- os.system('ifconfig %s down' % intf.name)
- os.system('ifconfig %s up' % intf.name)
+
+ self._del_hostapd(force=True)
+
os.system('hostapd -g %s -i %s %s &' %
(intf.ctrl_interface, intf.name, intf.config))
- # set flag so hostapd can be killed after the test
- self._hostapd_restarted = True
+ # Give hostapd a second to start and initialize the control interface
+ time.sleep(1)
+
+ # New hostapd process, so re-init
+ self._init_hostapd(intf)
def req_beacon(self, addr, request):
'''
--
2.21.1
1 year, 11 months
[PATCH 01/18] p2p: Stop discovery after GO Negotiation Req error
by Andrew Zaborowski
If we were in discovery only to be able to receive the target peer's
GO Negotiation Request (i.e. we have no users requesting discovery)
and we've received the frame and decided that the connection has
failed, exit discovery.
---
src/p2p.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/src/p2p.c b/src/p2p.c
index b8c299a3..095a05cd 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -1403,6 +1403,10 @@ static void p2p_device_go_negotiation_req_cb(const struct mmpdu_header *mpdu,
l_error("GO Negotiation Request parse error %s (%i)",
strerror(-r), -r);
p2p_connect_failed(dev);
+
+ if (l_queue_isempty(dev->discovery_users))
+ p2p_device_discovery_stop(dev);
+
status = P2P_STATUS_FAIL_INVALID_PARAMS;
goto respond;
}
@@ -1420,6 +1424,10 @@ static void p2p_device_go_negotiation_req_cb(const struct mmpdu_header *mpdu,
}
p2p_connect_failed(dev);
+
+ if (l_queue_isempty(dev->discovery_users))
+ p2p_device_discovery_stop(dev);
+
status = P2P_STATUS_FAIL_INCOMPATIBLE_PARAMS;
goto p2p_free;
}
@@ -1435,6 +1443,10 @@ static void p2p_device_go_negotiation_req_cb(const struct mmpdu_header *mpdu,
p2p_connect_failed(dev);
l_error("Persistent groups not supported");
+
+ if (l_queue_isempty(dev->discovery_users))
+ p2p_device_discovery_stop(dev);
+
status = P2P_STATUS_FAIL_INCOMPATIBLE_PARAMS;
goto p2p_free;
}
@@ -1442,6 +1454,10 @@ static void p2p_device_go_negotiation_req_cb(const struct mmpdu_header *mpdu,
if (req_info.device_password_id != dev->conn_password_id) {
p2p_connect_failed(dev);
l_error("Incompatible Password ID in the GO Negotiation Req");
+
+ if (l_queue_isempty(dev->discovery_users))
+ p2p_device_discovery_stop(dev);
+
status = P2P_STATUS_FAIL_INCOMPATIBLE_PROVISIONING;
goto p2p_free;
}
--
2.25.1
1 year, 11 months
[PATCH v6] netdev: use wiphy radio work queue for connections
by James Prestwood
This adds connection/FT attempts to the radio work queue. This
will ensure that connections aren't delayed or done concurrently
with scanning.
---
src/netdev.c | 220 +++++++++++++++++++++++++++++++++++---------------
src/station.c | 8 +-
2 files changed, 160 insertions(+), 68 deletions(-)
v6:
- Added address randomization to the work sequence since its taking
the interface down which would interfere with other wiphy work.
- Added checks in netdev_{disconnect,connect} if we have work already
in progress
- Fixed potential issue in netdev_disconnect where we could end up
not honoring the disconnect call if CMD_CONNECT had not yet been
been called back (meaning netdev->connect was not true). Instead
we can just check netdev->operational, the command ID, and work ID
to determine which state we are in.
- Reordered the call to netdev_disconnect in station because the
callback may be called immediately depending on the state which was
causing the station state to flip between disconnected/disconnecting
as station cleaned up the connection.
diff --git a/src/netdev.c b/src/netdev.c
index e469fb1c..b7571321 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -136,6 +136,9 @@ struct netdev {
struct l_io *pae_io; /* for drivers without EAPoL over NL80211 */
+ struct l_genl_msg *connect_cmd;
+ struct wiphy_radio_work_item work;
+
bool connected : 1;
bool operational : 1;
bool rekey_offload_support : 1;
@@ -485,6 +488,9 @@ static void netdev_preauth_destroy(void *data)
static void netdev_connect_free(struct netdev *netdev)
{
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
+
if (netdev->sm) {
eapol_sm_free(netdev->sm);
netdev->sm = NULL;
@@ -529,6 +535,7 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->in_ft = false;
netdev->ignore_connect_event = false;
netdev->expect_connect_failure = false;
+ netdev->connect_cmd = NULL;
netdev_rssi_polling_update(netdev);
@@ -1019,6 +1026,9 @@ static void netdev_connect_ok(struct netdev *netdev)
}
netdev_rssi_polling_update(netdev);
+
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
}
static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
@@ -1487,6 +1497,9 @@ void netdev_handshake_failed(struct handshake_state *hs, uint16_t reason_code)
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending DEL_STATION");
}
+
+ if (netdev->work.id)
+ wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
}
static void hardware_rekey_cb(struct l_genl_msg *msg, void *data)
@@ -2421,33 +2434,12 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct rtnl_data {
struct netdev *netdev;
- struct l_genl_msg *cmd_connect;
uint8_t addr[ETH_ALEN];
int ref;
};
-static int netdev_begin_connection(struct netdev *netdev,
- struct l_genl_msg *cmd_connect)
-{
- if (cmd_connect) {
- netdev->connect_cmd_id = l_genl_family_send(nl80211,
- cmd_connect, netdev_cmd_connect_cb,
- netdev, NULL);
-
- if (!netdev->connect_cmd_id) {
- l_genl_msg_unref(cmd_connect);
- return -EIO;
- }
- }
-
- handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+static int netdev_begin_connection(struct netdev *netdev);
- /* set connected since the auth protocols cannot do so internally */
- if (netdev->ap && auth_proto_start(netdev->ap))
- netdev->connected = true;
-
- return 0;
-}
static void netdev_mac_change_failed(struct netdev *netdev,
struct rtnl_data *req, int error)
@@ -2461,8 +2453,6 @@ static void netdev_mac_change_failed(struct netdev *netdev,
* mac_change_cmd_id was set.
*/
if (!netdev_get_is_up(netdev)) {
- l_genl_msg_unref(req->cmd_connect);
-
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
netdev, NETDEV_WATCH_EVENT_DOWN);
@@ -2474,7 +2464,7 @@ static void netdev_mac_change_failed(struct netdev *netdev,
*/
l_info("Interface still up after failing to change the MAC, "
"continuing with connection");
- if (netdev_begin_connection(netdev, req->cmd_connect) < 0)
+ if (netdev_begin_connection(netdev) < 0)
goto failed;
return;
@@ -2517,7 +2507,7 @@ static void netdev_mac_power_up_cb(int error, uint16_t type,
/*
* Pick up where we left off in netdev_connect_commmon.
*/
- if (netdev_begin_connection(netdev, req->cmd_connect) < 0) {
+ if (netdev_begin_connection(netdev) < 0) {
l_error("Failed to connect after changing MAC");
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
@@ -2576,9 +2566,7 @@ static void netdev_mac_power_down_cb(int error, uint16_t type,
* Returns -EALREADY if the requested MAC matched our current MAC
* Returns -EIO if there was an IO error when powering down
*/
-static int netdev_start_powered_mac_change(struct netdev *netdev,
- struct scan_bss *bss,
- struct l_genl_msg *cmd_connect)
+static int netdev_start_powered_mac_change(struct netdev *netdev)
{
struct rtnl_data *req;
uint8_t new_addr[6];
@@ -2586,8 +2574,8 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
/* No address set in handshake, use per-network MAC generation */
if (util_mem_is_zero(netdev->handshake->spa, ETH_ALEN))
wiphy_generate_address_from_ssid(netdev->wiphy,
- (const char *)bss->ssid,
- new_addr);
+ (const char *)netdev->handshake->ssid,
+ new_addr);
else
memcpy(new_addr, netdev->handshake->spa, ETH_ALEN);
@@ -2600,7 +2588,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
req = l_new(struct rtnl_data, 1);
req->netdev = netdev;
/* This message will need to be unreffed upon any error */
- req->cmd_connect = cmd_connect;
req->ref++;
memcpy(req->addr, new_addr, sizeof(req->addr));
@@ -2609,7 +2596,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
req, netdev_mac_destroy);
if (!netdev->mac_change_cmd_id) {
- l_genl_msg_unref(req->cmd_connect);
l_free(req);
return -EIO;
@@ -2618,6 +2604,105 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
return 0;
}
+static int netdev_begin_connection(struct netdev *netdev)
+{
+ /*
+ * Set the supplicant address now, this may have already been done for
+ * a non-randomized address connect, but if we are randomizing we need
+ * to set it again as the address should have now changed.
+ */
+ handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+
+ if (netdev->connect_cmd) {
+ netdev->connect_cmd_id = l_genl_family_send(nl80211,
+ netdev->connect_cmd,
+ netdev_cmd_connect_cb, netdev, NULL);
+
+ if (!netdev->connect_cmd_id)
+ goto failed;
+
+ netdev->connect_cmd = NULL;
+ }
+
+ return 0;
+
+failed:
+ if (netdev->connect_cmd) {
+ l_genl_msg_unref(netdev->connect_cmd);
+ netdev->connect_cmd = NULL;
+ }
+
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+
+ return -EIO;
+}
+
+static bool netdev_connection_ready(struct wiphy_radio_work_item *item)
+{
+ struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+ if (mac_per_ssid) {
+ int ret = netdev_start_powered_mac_change(netdev);
+
+ if (!ret)
+ return false;
+ else if (ret != -EALREADY)
+ goto failed;
+ }
+
+ if (netdev_begin_connection(netdev) < 0)
+ goto failed;
+
+ return false;
+
+failed:
+ if (netdev->connect_cmd) {
+ l_genl_msg_unref(netdev->connect_cmd);
+ netdev->connect_cmd = NULL;
+ }
+
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+
+ return true;
+}
+
+static const struct wiphy_radio_work_item_ops connect_work_ops = {
+ .do_work = netdev_connection_ready,
+};
+
+static bool netdev_ap_ready(struct wiphy_radio_work_item *item)
+{
+ struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+ handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+
+ if (!auth_proto_start(netdev->ap)) {
+ /* Restore original nonce if this was an FT attempt */
+ if (netdev->in_ft) {
+ memcpy(netdev->handshake->snonce,
+ netdev->prev_snonce, 32);
+
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+ return true;
+ }
+ }
+
+ /*
+ * set connected since the auth protocols cannot do
+ * so internally
+ */
+ netdev->connected = true;
+
+ return false;
+}
+
+static const struct wiphy_radio_work_item_ops ap_work_ops = {
+ .do_work = netdev_ap_ready,
+};
+
static int netdev_connect_common(struct netdev *netdev,
struct l_genl_msg *cmd_connect,
struct scan_bss *bss,
@@ -2626,6 +2711,7 @@ static int netdev_connect_common(struct netdev *netdev,
netdev_event_func_t event_filter,
netdev_connect_cb_t cb, void *user_data)
{
+ netdev->connect_cmd = cmd_connect;
netdev->event_filter = event_filter;
netdev->connect_cb = cb;
netdev->user_data = user_data;
@@ -2642,14 +2728,9 @@ static int netdev_connect_common(struct netdev *netdev,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
handshake_state_set_no_rekey(hs, true);
- if (mac_per_ssid) {
- int ret = netdev_start_powered_mac_change(netdev, bss,
- cmd_connect);
- if (ret != -EALREADY)
- return ret;
- }
-
- return netdev_begin_connection(netdev, cmd_connect);
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+ netdev->ap ? &ap_work_ops : &connect_work_ops);
+ return 0;
}
int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
@@ -2667,7 +2748,7 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
return -ENOTSUP;
- if (netdev->connected || netdev->connect_cmd_id)
+ if (netdev->connected || netdev->connect_cmd_id || netdev->work.id)
return -EISCONN;
switch (hs->akm_suite) {
@@ -2709,23 +2790,36 @@ int netdev_disconnect(struct netdev *netdev,
netdev_disconnect_cb_t cb, void *user_data)
{
struct l_genl_msg *disconnect;
+ bool send_disconnect = true;
if (netdev->type != NL80211_IFTYPE_STATION &&
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
return -ENOTSUP;
- if (!netdev->connected)
- return -ENOTCONN;
-
if (netdev->disconnect_cmd_id)
return -EINPROGRESS;
/* Only perform this if we haven't successfully fully associated yet */
if (!netdev->operational) {
+ /*
+ * Three possibilities here:
+ * 1. We do not actually have a connect in progress (work.id
+ * is zero), then we can bail out early with an error.
+ * 2. We have sent CMD_CONNECT but not fully connected. The
+ * CMD_CONNECT needs to be canceled and a disconnect should
+ * be sent.
+ * 3. Queued up the connect work, but haven't sent CMD_CONNECT
+ * to the kernel. This case we do not need to send a
+ * disconnect.
+ */
+ if (!netdev->work.id)
+ return -ENOTCONN;
+
if (netdev->connect_cmd_id) {
l_genl_family_cancel(nl80211, netdev->connect_cmd_id);
netdev->connect_cmd_id = 0;
- }
+ } else
+ send_disconnect = false;
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
MMPDU_REASON_CODE_UNSPECIFIED);
@@ -2733,19 +2827,23 @@ int netdev_disconnect(struct netdev *netdev,
netdev_connect_free(netdev);
}
- disconnect = netdev_build_cmd_disconnect(netdev,
+ if (send_disconnect) {
+ disconnect = netdev_build_cmd_disconnect(netdev,
MMPDU_REASON_CODE_DEAUTH_LEAVING);
- netdev->disconnect_cmd_id = l_genl_family_send(nl80211, disconnect,
- netdev_cmd_disconnect_cb, netdev, NULL);
+ netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
+ disconnect, netdev_cmd_disconnect_cb,
+ netdev, NULL);
- if (!netdev->disconnect_cmd_id) {
- l_genl_msg_unref(disconnect);
- return -EIO;
- }
+ if (!netdev->disconnect_cmd_id) {
+ l_genl_msg_unref(disconnect);
+ return -EIO;
+ }
- netdev->disconnect_cb = cb;
- netdev->user_data = user_data;
- netdev->aborting = true;
+ netdev->disconnect_cb = cb;
+ netdev->user_data = user_data;
+ netdev->aborting = true;
+ } else if (cb)
+ cb(netdev, true, user_data);
return 0;
}
@@ -3092,7 +3190,6 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_connect_cb_t cb)
{
struct netdev_handshake_state *nhs;
- int err = -EINVAL;
if (!netdev->operational)
return -ENOTCONN;
@@ -3173,15 +3270,10 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_ft_over_ds_tx_authenticate,
netdev_ft_tx_associate, netdev);
- if (!auth_proto_start(netdev->ap))
- goto restore_snonce;
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+ &ap_work_ops);
return 0;
-
-restore_snonce:
- memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
- return err;
}
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
diff --git a/src/station.c b/src/station.c
index 35f4a96d..0095d6c8 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2702,10 +2702,6 @@ int station_disconnect(struct station *station)
if (!station->connected_bss)
return -ENOTCONN;
- if (netdev_disconnect(station->netdev,
- station_disconnect_cb, station) < 0)
- return -EIO;
-
if (station->netconfig)
netconfig_reset(station->netconfig);
@@ -2718,6 +2714,10 @@ int station_disconnect(struct station *station)
station_enter_state(station, STATION_STATE_DISCONNECTING);
+ if (netdev_disconnect(station->netdev,
+ station_disconnect_cb, station) < 0)
+ return -EIO;
+
return 0;
}
--
2.21.1
1 year, 11 months
[PATCH] auto-t: fix hostapd.ungraceful_restart
by James Prestwood
This was killing hostapd, but not re-initializing any of the
control socket information so any use of hostapd.wait_for_event
after hostapd.ungraceful_restart would cause an exception.
---
autotests/util/hostapd.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/autotests/util/hostapd.py b/autotests/util/hostapd.py
index 49047f38..c3a4a0d0 100644
--- a/autotests/util/hostapd.py
+++ b/autotests/util/hostapd.py
@@ -193,6 +193,8 @@ class HostapdCLI:
'''
Ungracefully kill and restart hostapd
'''
+ self.ctrl_sock.close()
+
intf = hostapd_map[self.ifname]
os.system('killall -9 hostapd')
os.system('ifconfig %s down' % intf.name)
@@ -203,6 +205,9 @@ class HostapdCLI:
# set flag so hostapd can be killed after the test
self._hostapd_restarted = True
+ # New hostapd process, so re-init
+ self.__init__(intf)
+
def req_beacon(self, addr, request):
'''
Send a RRM Beacon request
--
2.21.1
1 year, 11 months
[PATCH v5 1/2] scan: refactor to use wiphy radio work queue
by James Prestwood
To use the wiphy radio work queue, scanning mostly remained the same.
start_next_scan_request was modified to be used as the work callback,
as well as not start the next scan if the current one was done
(since this is taken care of by wiphy work queue now). All
calls to start_next_scan_request were removed, and more or less
replaced with wiphy_radio_work_done.
scan_{suspend,resume} were both removed since radio management
priorities solve this for us. ANQP requests can be inserted ahead of
scan requests, which accomplishes the same thing.
---
src/scan.c | 140 ++++++++++++++++++++++----------------------------
src/scan.h | 3 --
src/station.c | 16 +-----
3 files changed, 61 insertions(+), 98 deletions(-)
v5:
- Fixed some logic errors with exernal scans and starting the next
internal scan.
diff --git a/src/scan.c b/src/scan.c
index 83804e57..c8761644 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -56,7 +56,8 @@
static struct l_queue *scan_contexts;
static struct l_genl_family *nl80211;
-static uint32_t next_scan_request_id;
+
+struct scan_context;
struct scan_periodic {
struct l_timeout *timeout;
@@ -70,7 +71,7 @@ struct scan_periodic {
};
struct scan_request {
- uint32_t id;
+ struct scan_context *sc;
scan_trigger_func_t trigger;
scan_notify_func_t callback;
void *userdata;
@@ -79,6 +80,7 @@ struct scan_request {
struct l_queue *cmds;
/* The time the current scan was started. Reported in TRIGGER_SCAN */
uint64_t start_time_tsf;
+ struct wiphy_radio_work_item work;
};
struct scan_context {
@@ -106,7 +108,7 @@ struct scan_context {
bool triggered:1;
/* Whether any commands from current request's queue have started */
bool started:1;
- bool suspended:1;
+ bool work_started:1;
struct wiphy *wiphy;
};
@@ -118,7 +120,7 @@ struct scan_results {
struct scan_request *sr;
};
-static bool start_next_scan_request(struct scan_context *sc);
+static bool start_next_scan_request(struct wiphy_radio_work_item *item);
static void scan_periodic_rearm(struct scan_context *sc);
static bool scan_context_match(const void *a, const void *b)
@@ -134,12 +136,13 @@ static bool scan_request_match(const void *a, const void *b)
const struct scan_request *sr = a;
uint32_t id = L_PTR_TO_UINT(b);
- return sr->id == id;
+ return sr->work.id == id;
}
-static void scan_request_free(void *data)
+static void scan_request_free(struct wiphy_radio_work_item *item)
{
- struct scan_request *sr = data;
+ struct scan_request *sr = l_container_of(item, struct scan_request,
+ work);
if (sr->destroy)
sr->destroy(sr->userdata);
@@ -159,12 +162,12 @@ static void scan_request_failed(struct scan_context *sc,
else if (sr->callback)
sr->callback(err, NULL, sr->userdata);
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
}
static struct scan_context *scan_context_new(uint64_t wdev_id)
{
- struct wiphy *wiphy = wiphy_find(wdev_id >> 32);
+ struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
struct scan_context *sc;
if (!wiphy)
@@ -180,11 +183,18 @@ static struct scan_context *scan_context_new(uint64_t wdev_id)
return sc;
}
+static void scan_request_cancel(void *data)
+{
+ struct scan_request *sr = data;
+
+ wiphy_radio_work_done(sr->sc->wiphy, sr->work.id);
+}
+
static void scan_context_free(struct scan_context *sc)
{
l_debug("sc: %p", sc);
- l_queue_destroy(sc->requests, scan_request_free);
+ l_queue_destroy(sc->requests, scan_request_cancel);
if (sc->sp.timeout)
l_timeout_remove(sc->sp.timeout);
@@ -215,7 +225,6 @@ static void scan_request_triggered(struct l_genl_msg *msg, void *userdata)
}
l_queue_remove(sc->requests, sr);
- start_next_scan_request(sc);
scan_request_failed(sc, sr, err);
@@ -517,6 +526,11 @@ static int scan_request_send_trigger(struct scan_context *sc,
return -EIO;
}
+static const struct wiphy_radio_work_item_ops work_ops = {
+ .do_work = start_next_scan_request,
+ .destroy = scan_request_free,
+};
+
static uint32_t scan_common(uint64_t wdev_id, bool passive,
const struct scan_parameters *params,
scan_trigger_func_t trigger,
@@ -532,36 +546,19 @@ static uint32_t scan_common(uint64_t wdev_id, bool passive,
return 0;
sr = l_new(struct scan_request, 1);
+ sr->sc = sc;
sr->trigger = trigger;
sr->callback = notify;
sr->userdata = userdata;
sr->destroy = destroy;
sr->passive = passive;
- sr->id = ++next_scan_request_id;
sr->cmds = l_queue_new();
scan_cmds_add(sr->cmds, sc, passive, params);
- /* Queue empty implies !sc->triggered && !sc->start_cmd_id */
- if (!l_queue_isempty(sc->requests))
- goto done;
-
- if (sc->suspended)
- goto done;
-
- if (sc->state != SCAN_STATE_NOT_RUNNING)
- goto done;
-
- if (!scan_request_send_trigger(sc, sr))
- goto done;
-
- sr->destroy = NULL; /* Don't call destroy when returning error */
- scan_request_free(sr);
- return 0;
-done:
l_queue_push_tail(sc->requests, sr);
- return sr->id;
+ return wiphy_radio_work_insert(sc->wiphy, &sr->work, 2, &work_ops);
}
uint32_t scan_passive(uint64_t wdev_id, struct scan_freq_set *freqs,
@@ -649,11 +646,11 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)
sc->start_cmd_id = 0;
l_queue_remove(sc->requests, sr);
sc->started = false;
- start_next_scan_request(sc);
} else
l_queue_remove(sc->requests, sr);
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
+
return true;
}
@@ -832,34 +829,30 @@ static void scan_periodic_rearm(struct scan_context *sc)
scan_periodic_timeout_destroy);
}
-static bool start_next_scan_request(struct scan_context *sc)
+static bool start_next_scan_request(struct wiphy_radio_work_item *item)
{
- struct scan_request *sr = l_queue_peek_head(sc->requests);
+ struct scan_request *sr = l_container_of(item,
+ struct scan_request, work);
+ struct scan_context *sc = sr->sc;
- if (sc->suspended)
- return true;
+ sc->work_started = true;
if (sc->state != SCAN_STATE_NOT_RUNNING)
- return true;
-
- if (sc->start_cmd_id || sc->get_scan_cmd_id)
- return true;
+ return false;
- while (sr) {
- if (!scan_request_send_trigger(sc, sr))
- return true;
+ if (!scan_request_send_trigger(sc, sr))
+ return false;
- scan_request_failed(sc, sr, -EIO);
+ sc->work_started = false;
- sr = l_queue_peek_head(sc->requests);
- }
+ scan_request_failed(sc, sr, -EIO);
if (sc->sp.retry) {
sc->sp.retry = false;
scan_periodic_queue(sc);
}
- return false;
+ return true;
}
static bool scan_parse_vendor_specific(struct scan_bss *bss, const void *data,
@@ -1500,6 +1493,7 @@ static void scan_finished(struct scan_context *sc,
if (sr) {
l_queue_remove(sc->requests, sr);
sc->started = false;
+ sc->work_started = false;
if (sr->callback)
new_owner = sr->callback(err, bss_list, sr->userdata);
@@ -1511,9 +1505,7 @@ static void scan_finished(struct scan_context *sc,
* taken care of sending the next command for a new or ongoing
* scan, or scheduling the next periodic scan.
*/
- start_next_scan_request(sc);
-
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
} else if (sc->sp.callback)
new_owner = sc->sp.callback(err, bss_list, sc->sp.userdata);
@@ -1677,9 +1669,21 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
sr = NULL;
}
- /* Send the next command of a new or an ongoing request */
- if (send_next)
- start_next_scan_request(sc);
+ /*
+ * Send the next command of an ongoing request, or continue with
+ * a previously busy scan attempt due to an external scan. A
+ * temporary scan_request object is used (rather than 'sr')
+ * because the state of 'sr' tells us if the results should
+ * be used either as normal scan results, or to take advantage
+ * of the external scan as a 'free' periodic scan of sorts.
+ */
+ if (sc->work_started && send_next) {
+ struct scan_request *next = l_queue_peek_head(
+ sc->requests);
+
+ if (next)
+ start_next_scan_request(&next->work);
+ }
if (!get_results)
break;
@@ -1720,6 +1724,7 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
if (sc->triggered) {
sc->triggered = false;
+ sc->work_started = false;
scan_finished(sc, -ECANCELED, NULL, sr);
} else {
@@ -1730,7 +1735,8 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
* hardware or the driver because of another activity
* starting in which case we should just get an EBUSY.
*/
- start_next_scan_request(sc);
+ if (sc->work_started)
+ start_next_scan_request(&sr->work);
}
break;
@@ -2133,32 +2139,6 @@ bool scan_wdev_remove(uint64_t wdev_id)
return true;
}
-bool scan_suspend(uint64_t wdev_id)
-{
- struct scan_context *sc;
-
- sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
- if (!sc)
- return false;
-
- sc->suspended = true;
-
- return true;
-}
-
-void scan_resume(uint64_t wdev_id)
-{
- struct scan_context *sc;
-
- sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
- if (!sc)
- return;
-
- sc->suspended = false;
-
- start_next_scan_request(sc);
-}
-
static int scan_init(void)
{
const struct l_settings *config = iwd_get_config();
diff --git a/src/scan.h b/src/scan.h
index aeeddf05..df0cc17c 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -170,6 +170,3 @@ bool scan_freq_set_isempty(const struct scan_freq_set *set);
bool scan_wdev_add(uint64_t wdev_id);
bool scan_wdev_remove(uint64_t wdev_id);
-
-bool scan_suspend(uint64_t wdev_id);
-void scan_resume(uint64_t wdev_id);
diff --git a/src/station.c b/src/station.c
index 0622e810..35f4a96d 100644
--- a/src/station.c
+++ b/src/station.c
@@ -531,8 +531,6 @@ request_done:
station_network_foreach(station, network_add_foreach, station);
station_autoconnect_next(station);
}
-
- scan_resume(netdev_get_wdev_id(station->netdev));
}
static bool station_start_anqp(struct station *station, struct network *network,
@@ -662,19 +660,7 @@ void station_set_scan_results(struct station *station,
l_hashmap_foreach_remove(station->networks, process_network, station);
- /*
- * ANQP requests are scheduled in the same manor as scans, and cannot
- * be done simultaneously. To avoid long queue times (waiting for a
- * scan to finish) its best to stop scanning, do ANQP, then resume
- * scanning.
- *
- * TODO: It may be possible for some hardware to actually scan and do
- * ANQP at the same time. Detecting this could allow us to continue
- * scanning.
- */
- if (wait_for_anqp)
- scan_suspend(netdev_get_wdev_id(station->netdev));
- else if (add_to_autoconnect) {
+ if (!wait_for_anqp && add_to_autoconnect) {
station_network_foreach(station, network_add_foreach, station);
station_autoconnect_next(station);
}
--
2.21.1
1 year, 11 months
[PATCH v4 1/2] scan: refactor to use wiphy radio work queue
by James Prestwood
To use the wiphy radio work queue, scanning mostly remained the same.
start_next_scan_request was modified to be used as the work callback,
as well as not start the next scan if the current one was done
(since this is taken care of by wiphy work queue now). All
calls to start_next_scan_request were removed, and more or less
replaced with wiphy_radio_work_done.
scan_{suspend,resume} were both removed since radio management
priorities solve this for us. ANQP requests can be inserted ahead of
scan requests, which accomplishes the same thing.
---
src/scan.c | 143 +++++++++++++++++++++-----------------------------
src/scan.h | 3 --
src/station.c | 16 +-----
3 files changed, 62 insertions(+), 100 deletions(-)
v4:
- Fixed some tricky logic inside the scan results to handle a busy
result due to external scans, as well as a race where an external
scan is triggered just before our own.
diff --git a/src/scan.c b/src/scan.c
index 83804e57..f1bb3646 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -56,7 +56,8 @@
static struct l_queue *scan_contexts;
static struct l_genl_family *nl80211;
-static uint32_t next_scan_request_id;
+
+struct scan_context;
struct scan_periodic {
struct l_timeout *timeout;
@@ -70,7 +71,7 @@ struct scan_periodic {
};
struct scan_request {
- uint32_t id;
+ struct scan_context *sc;
scan_trigger_func_t trigger;
scan_notify_func_t callback;
void *userdata;
@@ -79,6 +80,7 @@ struct scan_request {
struct l_queue *cmds;
/* The time the current scan was started. Reported in TRIGGER_SCAN */
uint64_t start_time_tsf;
+ struct wiphy_radio_work_item work;
};
struct scan_context {
@@ -106,7 +108,7 @@ struct scan_context {
bool triggered:1;
/* Whether any commands from current request's queue have started */
bool started:1;
- bool suspended:1;
+ bool work_started:1;
struct wiphy *wiphy;
};
@@ -118,7 +120,7 @@ struct scan_results {
struct scan_request *sr;
};
-static bool start_next_scan_request(struct scan_context *sc);
+static bool start_next_scan_request(struct wiphy_radio_work_item *item);
static void scan_periodic_rearm(struct scan_context *sc);
static bool scan_context_match(const void *a, const void *b)
@@ -134,12 +136,13 @@ static bool scan_request_match(const void *a, const void *b)
const struct scan_request *sr = a;
uint32_t id = L_PTR_TO_UINT(b);
- return sr->id == id;
+ return sr->work.id == id;
}
-static void scan_request_free(void *data)
+static void scan_request_free(struct wiphy_radio_work_item *item)
{
- struct scan_request *sr = data;
+ struct scan_request *sr = l_container_of(item, struct scan_request,
+ work);
if (sr->destroy)
sr->destroy(sr->userdata);
@@ -159,12 +162,12 @@ static void scan_request_failed(struct scan_context *sc,
else if (sr->callback)
sr->callback(err, NULL, sr->userdata);
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
}
static struct scan_context *scan_context_new(uint64_t wdev_id)
{
- struct wiphy *wiphy = wiphy_find(wdev_id >> 32);
+ struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
struct scan_context *sc;
if (!wiphy)
@@ -180,11 +183,18 @@ static struct scan_context *scan_context_new(uint64_t wdev_id)
return sc;
}
+static void scan_request_cancel(void *data)
+{
+ struct scan_request *sr = data;
+
+ wiphy_radio_work_done(sr->sc->wiphy, sr->work.id);
+}
+
static void scan_context_free(struct scan_context *sc)
{
l_debug("sc: %p", sc);
- l_queue_destroy(sc->requests, scan_request_free);
+ l_queue_destroy(sc->requests, scan_request_cancel);
if (sc->sp.timeout)
l_timeout_remove(sc->sp.timeout);
@@ -215,7 +225,6 @@ static void scan_request_triggered(struct l_genl_msg *msg, void *userdata)
}
l_queue_remove(sc->requests, sr);
- start_next_scan_request(sc);
scan_request_failed(sc, sr, err);
@@ -517,6 +526,11 @@ static int scan_request_send_trigger(struct scan_context *sc,
return -EIO;
}
+static const struct wiphy_radio_work_item_ops work_ops = {
+ .do_work = start_next_scan_request,
+ .destroy = scan_request_free,
+};
+
static uint32_t scan_common(uint64_t wdev_id, bool passive,
const struct scan_parameters *params,
scan_trigger_func_t trigger,
@@ -532,36 +546,19 @@ static uint32_t scan_common(uint64_t wdev_id, bool passive,
return 0;
sr = l_new(struct scan_request, 1);
+ sr->sc = sc;
sr->trigger = trigger;
sr->callback = notify;
sr->userdata = userdata;
sr->destroy = destroy;
sr->passive = passive;
- sr->id = ++next_scan_request_id;
sr->cmds = l_queue_new();
scan_cmds_add(sr->cmds, sc, passive, params);
- /* Queue empty implies !sc->triggered && !sc->start_cmd_id */
- if (!l_queue_isempty(sc->requests))
- goto done;
-
- if (sc->suspended)
- goto done;
-
- if (sc->state != SCAN_STATE_NOT_RUNNING)
- goto done;
-
- if (!scan_request_send_trigger(sc, sr))
- goto done;
-
- sr->destroy = NULL; /* Don't call destroy when returning error */
- scan_request_free(sr);
- return 0;
-done:
l_queue_push_tail(sc->requests, sr);
- return sr->id;
+ return wiphy_radio_work_insert(sc->wiphy, &sr->work, 2, &work_ops);
}
uint32_t scan_passive(uint64_t wdev_id, struct scan_freq_set *freqs,
@@ -622,7 +619,7 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)
if (!sr)
return false;
- /* If already triggered, just zero out the callback */
+ /* If already triggered, just zero out the callback. */
if (sr == l_queue_peek_head(sc->requests) && sc->triggered) {
l_debug("Scan is at the top of the queue and triggered");
@@ -649,11 +646,11 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)
sc->start_cmd_id = 0;
l_queue_remove(sc->requests, sr);
sc->started = false;
- start_next_scan_request(sc);
} else
l_queue_remove(sc->requests, sr);
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
+
return true;
}
@@ -832,34 +829,30 @@ static void scan_periodic_rearm(struct scan_context *sc)
scan_periodic_timeout_destroy);
}
-static bool start_next_scan_request(struct scan_context *sc)
+static bool start_next_scan_request(struct wiphy_radio_work_item *item)
{
- struct scan_request *sr = l_queue_peek_head(sc->requests);
+ struct scan_request *sr = l_container_of(item,
+ struct scan_request, work);
+ struct scan_context *sc = sr->sc;
- if (sc->suspended)
- return true;
+ sc->work_started = true;
if (sc->state != SCAN_STATE_NOT_RUNNING)
- return true;
-
- if (sc->start_cmd_id || sc->get_scan_cmd_id)
- return true;
+ return false;
- while (sr) {
- if (!scan_request_send_trigger(sc, sr))
- return true;
+ if (!scan_request_send_trigger(sc, sr))
+ return false;
- scan_request_failed(sc, sr, -EIO);
+ sc->work_started = false;
- sr = l_queue_peek_head(sc->requests);
- }
+ scan_request_failed(sc, sr, -EIO);
if (sc->sp.retry) {
sc->sp.retry = false;
scan_periodic_queue(sc);
}
- return false;
+ return true;
}
static bool scan_parse_vendor_specific(struct scan_bss *bss, const void *data,
@@ -1511,9 +1504,7 @@ static void scan_finished(struct scan_context *sc,
* taken care of sending the next command for a new or ongoing
* scan, or scheduling the next periodic scan.
*/
- start_next_scan_request(sc);
-
- scan_request_free(sr);
+ wiphy_radio_work_done(sc->wiphy, sr->work.id);
} else if (sc->sp.callback)
new_owner = sc->sp.callback(err, bss_list, sc->sp.userdata);
@@ -1645,6 +1636,7 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
/* Was this our own scan or an external scan */
if (sc->triggered) {
sc->triggered = false;
+ sc->work_started = false;
if (!sr->callback) {
scan_finished(sc, -ECANCELED, NULL, sr);
@@ -1677,9 +1669,20 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
sr = NULL;
}
- /* Send the next command of a new or an ongoing request */
- if (send_next)
- start_next_scan_request(sc);
+ /*
+ * Send the next command of an ongoing request, or continue with
+ * a previously busy scan attempt due to an external scan. A
+ * temporary scan_request object is used (rather than 'sr')
+ * because the state of 'sr' tells us if the results should
+ * be used either as normal scan results, or to take advantage
+ * of the external scan as a 'free' periodic scan of sorts.
+ */
+ if (sc->work_started || send_next) {
+ struct scan_request *next = l_queue_peek_head(
+ sc->requests);
+
+ start_next_scan_request(&next->work);
+ }
if (!get_results)
break;
@@ -1720,17 +1723,19 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
if (sc->triggered) {
sc->triggered = false;
+ sc->work_started = false;
scan_finished(sc, -ECANCELED, NULL, sr);
} else {
- /*
+ /*
* If this was an external scan that got aborted
* we may be able to now queue our own scan although
* the abort could also have been triggered by the
* hardware or the driver because of another activity
* starting in which case we should just get an EBUSY.
*/
- start_next_scan_request(sc);
+ if (sc->work_started)
+ start_next_scan_request(&sr->work);
}
break;
@@ -2133,32 +2138,6 @@ bool scan_wdev_remove(uint64_t wdev_id)
return true;
}
-bool scan_suspend(uint64_t wdev_id)
-{
- struct scan_context *sc;
-
- sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
- if (!sc)
- return false;
-
- sc->suspended = true;
-
- return true;
-}
-
-void scan_resume(uint64_t wdev_id)
-{
- struct scan_context *sc;
-
- sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
- if (!sc)
- return;
-
- sc->suspended = false;
-
- start_next_scan_request(sc);
-}
-
static int scan_init(void)
{
const struct l_settings *config = iwd_get_config();
diff --git a/src/scan.h b/src/scan.h
index aeeddf05..df0cc17c 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -170,6 +170,3 @@ bool scan_freq_set_isempty(const struct scan_freq_set *set);
bool scan_wdev_add(uint64_t wdev_id);
bool scan_wdev_remove(uint64_t wdev_id);
-
-bool scan_suspend(uint64_t wdev_id);
-void scan_resume(uint64_t wdev_id);
diff --git a/src/station.c b/src/station.c
index 0622e810..35f4a96d 100644
--- a/src/station.c
+++ b/src/station.c
@@ -531,8 +531,6 @@ request_done:
station_network_foreach(station, network_add_foreach, station);
station_autoconnect_next(station);
}
-
- scan_resume(netdev_get_wdev_id(station->netdev));
}
static bool station_start_anqp(struct station *station, struct network *network,
@@ -662,19 +660,7 @@ void station_set_scan_results(struct station *station,
l_hashmap_foreach_remove(station->networks, process_network, station);
- /*
- * ANQP requests are scheduled in the same manor as scans, and cannot
- * be done simultaneously. To avoid long queue times (waiting for a
- * scan to finish) its best to stop scanning, do ANQP, then resume
- * scanning.
- *
- * TODO: It may be possible for some hardware to actually scan and do
- * ANQP at the same time. Detecting this could allow us to continue
- * scanning.
- */
- if (wait_for_anqp)
- scan_suspend(netdev_get_wdev_id(station->netdev));
- else if (add_to_autoconnect) {
+ if (!wait_for_anqp && add_to_autoconnect) {
station_network_foreach(station, network_add_foreach, station);
station_autoconnect_next(station);
}
--
2.21.1
1 year, 11 months
[PATCH v3 1/6] wiphy: introduce new radio management APIs
by James Prestwood
These APIs will handle fairness and order in any operations which
radios can only do sequentially (offchannel, scanning, connection etc.).
Both scan and frame-xchg are complex modules (especially scanning)
which is why the radio management APIs were implemented generic enough
where the changes to both modules will be minimal. Any module that
requires this kind of work can push a work item into the radio
management work queue (wiphy_radio_work_insert) and when the work
is ready to be started radio management will call back into the module.
Once the work is completed (and this may be some time later e.g. in
scan results or a frame watch) the module can signal back that the
work is finished (wiphy_radio_work_done). Wiphy will then pop the
queue and continue with the next work item.
A concept of priority was added in order to allow important offchannel
operations (e.g. ANQP) to take priority over other work items. The
priority is an integer, where lower values are of a higher priority.
The concept of priority cleanly solves a lot of the complexity that
was added in order to support ANQP queries (suspending scanning and
waiting for ANQP to finish before connecting).
Instead ANQP queries can be queued at a higher priority than scanning
which removes the need for suspending scans. In addition we can treat
connections as radio management work and insert them at a lower
priority than ANQP, but higher than scanning. This forces the
connection to wait for ANQP without having to track any state.
---
src/wiphy.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/wiphy.h | 22 +++++++++++
2 files changed, 134 insertions(+)
v2:
- Better optimized wiphy_radio_work_done to not iterate the queue
more than it needs to.
diff --git a/src/wiphy.c b/src/wiphy.c
index aef39549..47be2c85 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -32,6 +32,7 @@
#include <fnmatch.h>
#include <unistd.h>
#include <string.h>
+#include <limits.h>
#include <ell/ell.h>
@@ -62,6 +63,7 @@ static char **whitelist_filter;
static char **blacklist_filter;
static int mac_randomize_bytes = 6;
static char regdom_country[2];
+static uint32_t work_ids;
struct wiphy {
uint32_t id;
@@ -85,6 +87,8 @@ struct wiphy {
uint8_t rm_enabled_capabilities[7]; /* 5 size max + header */
struct l_genl_family *nl80211;
char regdom_country[2];
+ /* Work queue for this radio */
+ struct l_queue *work;
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
@@ -216,6 +220,14 @@ static struct wiphy *wiphy_new(uint32_t id)
return wiphy;
}
+static void destroy_work(void *user_data)
+{
+ struct wiphy_radio_work_item *work = user_data;
+
+ if (work->ops && work->ops->destroy)
+ work->ops->destroy(work);
+}
+
static void wiphy_free(void *data)
{
struct wiphy *wiphy = data;
@@ -235,6 +247,7 @@ static void wiphy_free(void *data)
l_free(wiphy->vendor_str);
l_free(wiphy->driver_str);
l_genl_family_free(wiphy->nl80211);
+ l_queue_destroy(wiphy->work, destroy_work);
l_free(wiphy);
}
@@ -1072,6 +1085,8 @@ struct wiphy *wiphy_create(uint32_t wiphy_id, const char *name)
if (!wiphy_is_managed(name))
wiphy->blacklisted = true;
+ wiphy->work = l_queue_new();
+
return wiphy;
}
@@ -1449,6 +1464,103 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
}
}
+static void wiphy_radio_work_next(struct wiphy *wiphy)
+{
+ struct wiphy_radio_work_item *work;
+ bool done;
+
+ work = l_queue_peek_head(wiphy->work);
+ if (!work)
+ return;
+
+ /*
+ * Ensures no other work item will get inserted before this one while
+ * the work is being done.
+ */
+ work->priority = INT_MIN;
+
+ l_debug("Starting work item %u", work->id);
+ done = work->ops->do_work(work);
+
+ if (done) {
+ work->id = 0;
+
+ l_queue_remove(wiphy->work, work);
+
+ destroy_work(work);
+
+ wiphy_radio_work_next(wiphy);
+ }
+}
+
+static int insert_by_priority(const void *a, const void *b, void *user_data)
+{
+ const struct wiphy_radio_work_item *new = a;
+ const struct wiphy_radio_work_item *work = b;
+
+ if (work->priority <= new->priority)
+ return 1;
+
+ return -1;
+}
+
+uint32_t wiphy_radio_work_insert(struct wiphy *wiphy,
+ struct wiphy_radio_work_item *item,
+ int priority,
+ const struct wiphy_radio_work_item_ops *ops)
+{
+ item->priority = priority;
+ item->ops = ops;
+ item->id = ++work_ids;
+
+ l_debug("Inserting work item %u", item->id);
+
+ l_queue_insert(wiphy->work, item, insert_by_priority, NULL);
+
+ if (l_queue_length(wiphy->work) == 1)
+ wiphy_radio_work_next(wiphy);
+
+ return item->id;
+}
+
+static bool match_id(const void *a, const void *b)
+{
+ const struct wiphy_radio_work_item *item = a;
+
+ if (item->id == L_PTR_TO_UINT(b))
+ return true;
+
+ return false;
+}
+
+void wiphy_radio_work_done(struct wiphy *wiphy, uint32_t id)
+{
+ struct wiphy_radio_work_item *item;
+ bool next = false;
+
+ item = l_queue_peek_head(wiphy->work);
+ if (!item)
+ return;
+
+ if (item->id == id) {
+ next = true;
+ l_queue_pop_head(wiphy->work);
+ } else
+ item = l_queue_remove_if(wiphy->work, match_id,
+ L_UINT_TO_PTR(id));
+ if (!item)
+ return;
+
+ l_debug("Work item %u done", id);
+
+ item->id = 0;
+
+ destroy_work(item);
+
+ if (next)
+ wiphy_radio_work_next(wiphy);
+}
+
static int wiphy_init(void)
{
struct l_genl *genl = iwd_get_genl();
diff --git a/src/wiphy.h b/src/wiphy.h
index 689f868b..50c8c936 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -26,6 +26,22 @@
struct wiphy;
struct scan_bss;
struct scan_freq_set;
+struct wiphy_radio_work_item;
+
+typedef bool (*wiphy_radio_work_func_t)(struct wiphy_radio_work_item *item);
+typedef void (*wiphy_radio_work_destroy_func_t)(
+ struct wiphy_radio_work_item *item);
+
+struct wiphy_radio_work_item_ops {
+ wiphy_radio_work_func_t do_work;
+ wiphy_radio_work_destroy_func_t destroy;
+};
+
+struct wiphy_radio_work_item {
+ uint32_t id;
+ int priority;
+ const struct wiphy_radio_work_item_ops *ops;
+};
enum wiphy_state_watch_event {
WIPHY_STATE_WATCH_EVENT_POWERED,
@@ -92,3 +108,9 @@ uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func, void *user_data,
wiphy_destroy_func_t destroy);
bool wiphy_state_watch_remove(struct wiphy *wiphy, uint32_t id);
+
+uint32_t wiphy_radio_work_insert(struct wiphy *wiphy,
+ struct wiphy_radio_work_item *item,
+ int priority,
+ const struct wiphy_radio_work_item_ops *ops);
+void wiphy_radio_work_done(struct wiphy *wiphy, uint32_t id);
--
2.21.1
1 year, 11 months
[PATCH 1/2] auto-t: increase wait time for blacklist test
by James Prestwood
This test was not reliably passing. Busy waiting is not really reliable,
but in this specific case its really the only option as the blacklist
must expire based on time.
---
autotests/testBSSBlacklist/connection_test.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/autotests/testBSSBlacklist/connection_test.py b/autotests/testBSSBlacklist/connection_test.py
index 917f3192..7433d5f3 100644
--- a/autotests/testBSSBlacklist/connection_test.py
+++ b/autotests/testBSSBlacklist/connection_test.py
@@ -48,9 +48,11 @@ class Test(unittest.TestCase):
psk_agent = PSKAgent("secret123")
wd.register_psk_agent(psk_agent)
- devices = wd.list_devices(1)
+ devices = wd.list_devices(2)
device = devices[0]
+ devices[1].disconnect()
+
condition = 'not obj.scanning'
wd.wait_for_object_condition(device, condition)
@@ -110,8 +112,8 @@ class Test(unittest.TestCase):
# Wait for the blacklist to expire (10 seconds)
elapsed = time.time() - start
- if elapsed < 11:
- time.sleep(11 - elapsed)
+ if elapsed < 15:
+ wd.wait(15 - elapsed)
device.disconnect()
--
2.21.1
1 year, 11 months
[PATCH v2 1/6] wiphy: introduce new radio management APIs
by James Prestwood
These APIs will handle fairness and order in any operations which
radios can only do sequentially (offchannel, scanning, connection etc.).
Both scan and frame-xchg are complex modules (especially scanning)
which is why the radio management APIs were implemented generic enough
where the changes to both modules will be minimal. Any module that
requires this kind of work can push a work item into the radio
management work queue (wiphy_radio_work_insert) and when the work
is ready to be started radio management will call back into the module.
Once the work is completed (and this may be some time later e.g. in
scan results or a frame watch) the module can signal back that the
work is finished (wiphy_radio_work_done). Wiphy will then pop the
queue and continue with the next work item.
A concept of priority was added in order to allow important offchannel
operations (e.g. ANQP) to take priority over other work items. The
priority is an integer, where lower values are of a higher priority.
The concept of priority cleanly solves a lot of the complexity that
was added in order to support ANQP queries (suspending scanning and
waiting for ANQP to finish before connecting).
Instead ANQP queries can be queued at a higher priority than scanning
which removes the need for suspending scans. In addition we can treat
connections as radio management work and insert them at a lower
priority than ANQP, but higher than scanning. This forces the
connection to wait for ANQP without having to track any state.
---
src/wiphy.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/wiphy.h | 22 +++++++++++
2 files changed, 133 insertions(+)
v2:
- Removed in_cb/stale members. This disallows the caller from signaling
work done inside the work callback.
- Cleaned up some old radio_mgmt naming
- Remove 'count' concept when inserting work items. Instead the priority
of the current work item is set to INT_MIN which will ensure no other
work item gets inserted ahead of the current item.
diff --git a/src/wiphy.c b/src/wiphy.c
index aef39549..2e2693cf 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -32,6 +32,7 @@
#include <fnmatch.h>
#include <unistd.h>
#include <string.h>
+#include <limits.h>
#include <ell/ell.h>
@@ -62,6 +63,7 @@ static char **whitelist_filter;
static char **blacklist_filter;
static int mac_randomize_bytes = 6;
static char regdom_country[2];
+static uint32_t work_ids;
struct wiphy {
uint32_t id;
@@ -85,6 +87,8 @@ struct wiphy {
uint8_t rm_enabled_capabilities[7]; /* 5 size max + header */
struct l_genl_family *nl80211;
char regdom_country[2];
+ /* Work queue for this radio */
+ struct l_queue *work;
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
@@ -216,6 +220,14 @@ static struct wiphy *wiphy_new(uint32_t id)
return wiphy;
}
+static void destroy_work(void *user_data)
+{
+ struct wiphy_radio_work_item *work = user_data;
+
+ if (work->ops && work->ops->destroy)
+ work->ops->destroy(work);
+}
+
static void wiphy_free(void *data)
{
struct wiphy *wiphy = data;
@@ -235,6 +247,7 @@ static void wiphy_free(void *data)
l_free(wiphy->vendor_str);
l_free(wiphy->driver_str);
l_genl_family_free(wiphy->nl80211);
+ l_queue_destroy(wiphy->work, destroy_work);
l_free(wiphy);
}
@@ -1072,6 +1085,8 @@ struct wiphy *wiphy_create(uint32_t wiphy_id, const char *name)
if (!wiphy_is_managed(name))
wiphy->blacklisted = true;
+ wiphy->work = l_queue_new();
+
return wiphy;
}
@@ -1449,6 +1464,102 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
}
}
+static void wiphy_radio_work_next(struct wiphy *wiphy)
+{
+ struct wiphy_radio_work_item *work;
+ bool done;
+
+ work = l_queue_peek_head(wiphy->work);
+ if (!work)
+ return;
+
+ /*
+ * Ensures no other work item will get inserted before this one while
+ * the work is being done.
+ */
+ work->priority = INT_MIN;
+
+ l_debug("Starting work item %u", work->id);
+ done = work->ops->do_work(work);
+
+ if (done) {
+ work->id = 0;
+
+ l_queue_remove(wiphy->work, work);
+
+ destroy_work(work);
+
+ wiphy_radio_work_next(wiphy);
+ }
+}
+
+static int insert_by_priority(const void *a, const void *b, void *user_data)
+{
+ const struct wiphy_radio_work_item *new = a;
+ const struct wiphy_radio_work_item *work = b;
+
+ if (work->priority <= new->priority)
+ return 1;
+
+ return -1;
+}
+
+uint32_t wiphy_radio_work_insert(struct wiphy *wiphy,
+ struct wiphy_radio_work_item *item,
+ int priority,
+ const struct wiphy_radio_work_item_ops *ops)
+{
+ item->priority = priority;
+ item->ops = ops;
+ item->id = ++work_ids;
+
+ l_debug("Inserting work item %u", item->id);
+
+ l_queue_insert(wiphy->work, item, insert_by_priority, NULL);
+
+ if (l_queue_length(wiphy->work) == 1)
+ wiphy_radio_work_next(wiphy);
+
+ return item->id;
+}
+
+static bool match_id(const void *a, const void *b)
+{
+ const struct wiphy_radio_work_item *item = a;
+
+ if (item->id == L_PTR_TO_UINT(b))
+ return true;
+
+ return false;
+}
+
+void wiphy_radio_work_done(struct wiphy *wiphy, uint32_t id)
+{
+ struct wiphy_radio_work_item *item;
+ bool next = false;
+
+ item = l_queue_find(wiphy->work, match_id, L_UINT_TO_PTR(id));
+ if (!item)
+ return;
+
+ l_debug("Work item %u done", id);
+
+ item->id = 0;
+
+ /*
+ * This is the current work item so we can continue onto the next.
+ */
+ if (item == l_queue_peek_head(wiphy->work))
+ next = true;
+
+ l_queue_remove(wiphy->work, item);
+
+ destroy_work(item);
+
+ if (next)
+ wiphy_radio_work_next(wiphy);
+}
+
static int wiphy_init(void)
{
struct l_genl *genl = iwd_get_genl();
diff --git a/src/wiphy.h b/src/wiphy.h
index 689f868b..50c8c936 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -26,6 +26,22 @@
struct wiphy;
struct scan_bss;
struct scan_freq_set;
+struct wiphy_radio_work_item;
+
+typedef bool (*wiphy_radio_work_func_t)(struct wiphy_radio_work_item *item);
+typedef void (*wiphy_radio_work_destroy_func_t)(
+ struct wiphy_radio_work_item *item);
+
+struct wiphy_radio_work_item_ops {
+ wiphy_radio_work_func_t do_work;
+ wiphy_radio_work_destroy_func_t destroy;
+};
+
+struct wiphy_radio_work_item {
+ uint32_t id;
+ int priority;
+ const struct wiphy_radio_work_item_ops *ops;
+};
enum wiphy_state_watch_event {
WIPHY_STATE_WATCH_EVENT_POWERED,
@@ -92,3 +108,9 @@ uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func, void *user_data,
wiphy_destroy_func_t destroy);
bool wiphy_state_watch_remove(struct wiphy *wiphy, uint32_t id);
+
+uint32_t wiphy_radio_work_insert(struct wiphy *wiphy,
+ struct wiphy_radio_work_item *item,
+ int priority,
+ const struct wiphy_radio_work_item_ops *ops);
+void wiphy_radio_work_done(struct wiphy *wiphy, uint32_t id);
--
2.21.1
1 year, 11 months