All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/6] wiphy: introduce new radio management APIs
@ 2020-07-09  0:04 James Prestwood
  2020-07-09  0:04 ` [PATCH v3 2/6] frame-xchg: refactor to use wiphy work queue James Prestwood
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 6744 bytes --]

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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 2/6] frame-xchg: refactor to use wiphy work queue
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
@ 2020-07-09  0:04 ` James Prestwood
  2020-07-09  0:04 ` [PATCH v3 3/6] anqp: refactor to use frame-xchg James Prestwood
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 12438 bytes --]

In order to first integrate frame-xchg some refactoring needed to
be done. First it is useful to allow queueing frames up rather than
requiring the module (p2p, anqp etc) to wait for the last frame to
finish. This can be aided by radio management but frame-xchg needed
some refactoring as well.

First was getting rid of this fx pointer re-use. It looks like this
was done to save a bit of memory but things get pretty complex
needed to check if the pointer is stale or has been reset. Instead
of this we now just allocate a new pointer each frame-xchg. This
allows for the module to queue multiple requests as well as removes
the complexity of needed to check if the fx pointer is stale.

Next was adding the ability to track frame-xchgs by ID. If a module
can queue up multiple requests it also needs to be able to cancel
them individually vs per-wdev. This comes free with the wiphy work
queue since it returns an ID which can be given directly to the
caller.

Then radio management was simply piped in by adding the
insert/done APIs.
---
 src/frame-xchg.c | 121 +++++++++++++++++++++++------------------------
 src/frame-xchg.h |   6 +--
 src/p2p.c        |  22 +++++++--
 3 files changed, 81 insertions(+), 68 deletions(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index a9c1e4c3..1ba2d907 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -41,6 +41,7 @@
 #include "src/nl80211util.h"
 #include "src/netdev.h"
 #include "src/frame-xchg.h"
+#include "src/wiphy.h"
 
 #ifndef SOL_NETLINK
 #define SOL_NETLINK 270
@@ -113,8 +114,7 @@ struct frame_xchg_data {
 	unsigned int retry_interval;
 	unsigned int resp_timeout;
 	unsigned int retries_on_ack;
-	bool in_frame_cb : 1;
-	bool stale : 1;
+	struct wiphy_radio_work_item work;
 };
 
 struct frame_xchg_watch_data {
@@ -726,7 +726,7 @@ static bool frame_watch_remove_by_handler(uint64_t wdev_id, uint32_t group_id,
 					&handler_info) > 0;
 }
 
-static void frame_xchg_tx_retry(struct frame_xchg_data *fx);
+static bool frame_xchg_tx_retry(struct wiphy_radio_work_item *item);
 static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
 					const void *body, size_t body_len,
 					int rssi, void *user_data);
@@ -753,9 +753,6 @@ static void frame_xchg_wait_cancel(struct frame_xchg_data *fx)
 
 static void frame_xchg_reset(struct frame_xchg_data *fx)
 {
-	fx->in_frame_cb = false;
-	fx->stale = false;
-
 	frame_xchg_wait_cancel(fx);
 
 	if (fx->timeout)
@@ -771,9 +768,10 @@ static void frame_xchg_reset(struct frame_xchg_data *fx)
 					frame_xchg_resp_cb, fx);
 }
 
-static void frame_xchg_destroy(void *user_data)
+static void frame_xchg_destroy(struct wiphy_radio_work_item *item)
 {
-	struct frame_xchg_data *fx = user_data;
+	struct frame_xchg_data *fx = l_container_of(item,
+						struct frame_xchg_data, work);
 
 	if (fx->destroy)
 		fx->destroy(fx->user_data);
@@ -789,7 +787,7 @@ static void frame_xchg_done(struct frame_xchg_data *fx, int err)
 	if (fx->cb)
 		fx->cb(err, fx->user_data);
 
-	frame_xchg_destroy(fx);
+	wiphy_radio_work_done(wiphy_find_by_wdev(fx->wdev_id), fx->work.id);
 }
 
 static void frame_xchg_timeout_destroy(void *user_data)
@@ -805,7 +803,7 @@ static void frame_xchg_timeout_cb(struct l_timeout *timeout,
 	struct frame_xchg_data *fx = user_data;
 
 	l_timeout_remove(fx->timeout);
-	frame_xchg_tx_retry(fx);
+	frame_xchg_tx_retry(&fx->work);
 }
 
 static void frame_xchg_listen_end_cb(struct l_timeout *timeout,
@@ -821,7 +819,7 @@ static void frame_xchg_listen_end_cb(struct l_timeout *timeout,
 	l_timeout_remove(fx->timeout);
 	fx->retries_on_ack--;
 	fx->retry_cnt = 0;
-	frame_xchg_tx_retry(fx);
+	frame_xchg_tx_retry(&fx->work);
 }
 
 static void frame_xchg_tx_status(struct frame_xchg_data *fx, bool acked)
@@ -918,8 +916,10 @@ error:
 	frame_xchg_done(fx, error);
 }
 
-static void frame_xchg_tx_retry(struct frame_xchg_data *fx)
+static bool frame_xchg_tx_retry(struct wiphy_radio_work_item *item)
 {
+	struct frame_xchg_data *fx = l_container_of(item,
+						struct frame_xchg_data, work);
 	struct l_genl_msg *msg;
 	uint32_t cmd_id;
 	uint32_t duration = fx->resp_timeout;
@@ -951,13 +951,15 @@ static void frame_xchg_tx_retry(struct frame_xchg_data *fx)
 		l_error("Error sending frame");
 		l_genl_msg_unref(msg);
 		frame_xchg_done(fx, -EIO);
-		return;
+		return true;
 	}
 
 	fx->tx_acked = false;
 	fx->have_cookie = false;
 	fx->early_status = false;
 	fx->retry_cnt++;
+
+	return false;
 }
 
 static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
@@ -999,20 +1001,10 @@ static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
 		if (!fx->tx_acked)
 			goto early_frame;
 
-		fx->in_frame_cb = true;
 		done = watch->cb(mpdu, body, body_len, rssi, fx->user_data);
 
-		/*
-		 * If the callback has started a new frame exchange it will
-		 * have reset and taken over the state variables and we need
-		 * to just exit without touching anything.
-		 */
-		if (!fx->in_frame_cb)
-			return true;
-
-		fx->in_frame_cb = false;
-
-		if (done || fx->stale) {
+		if (done) {
+			/* NULL callback here since the caller is done */
 			fx->cb = NULL;
 			frame_xchg_done(fx, 0);
 			return true;
@@ -1070,22 +1062,29 @@ static bool frame_xchg_match(const void *a, const void *b)
  * @resp_timeout was 0.  @frame is an iovec array terminated by an iovec
  * struct with NULL-iov_base.
  */
-void frame_xchg_start(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
+uint32_t frame_xchg_start(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 			unsigned int retry_interval, unsigned int resp_timeout,
 			unsigned int retries_on_ack, uint32_t group_id,
 			frame_xchg_cb_t cb, void *user_data,
 			frame_xchg_destroy_func_t destroy, ...)
 {
+	uint32_t id;
 	va_list args;
 
 	va_start(args, destroy);
-	frame_xchg_startv(wdev_id, frame, freq, retry_interval, resp_timeout,
+	id = frame_xchg_startv(wdev_id, frame, freq, retry_interval, resp_timeout,
 				retries_on_ack, group_id, cb, user_data,
 				destroy, args);
 	va_end(args);
+	return id;
 }
 
-void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
+static const struct wiphy_radio_work_item_ops work_ops = {
+	.do_work = frame_xchg_tx_retry,
+	.destroy = frame_xchg_destroy,
+};
+
+uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 			unsigned int retry_interval, unsigned int resp_timeout,
 			unsigned int retries_on_ack, uint32_t group_id,
 			frame_xchg_cb_t cb, void *user_data,
@@ -1108,31 +1107,13 @@ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 				frame[0].iov_base)) {
 		l_error("Frame too short");
 		cb(-EMSGSIZE, user_data);
-		return;
+		return 0;
 	}
 
-	fx = l_queue_find(frame_xchgs, frame_xchg_match, &wdev_id);
-
-	if (fx) {
-		/*
-		 * If a frame callback calls us assume it's the end of
-		 * that earlier frame exchange and the start of a new one.
-		 */
-		if (fx->in_frame_cb)
-			frame_xchg_reset(fx);
-		else {
-			l_error("Frame exchange in progress");
-			cb(-EBUSY, user_data);
-			return;
-		}
-	} else {
-		fx = l_new(struct frame_xchg_data, 1);
+	fx = l_new(struct frame_xchg_data, 1);
 
-		if (!frame_xchgs)
-			frame_xchgs = l_queue_new();
-
-		l_queue_push_tail(frame_xchgs, fx);
-	}
+	if (!frame_xchgs)
+		frame_xchgs = l_queue_new();
 
 	fx->wdev_id = wdev_id;
 	fx->freq = freq;
@@ -1177,24 +1158,35 @@ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 	}
 
 	fx->retry_cnt = 0;
-	frame_xchg_tx_retry(fx);
+
+	l_queue_push_tail(frame_xchgs, fx);
+
+	/*
+	 * TODO: Assume any offchannel frames are a high priority (0). This may
+	 * need to be re-examined in the future if other operations (e.g.
+	 * wait on channel) are introduced.
+	 */
+	return wiphy_radio_work_insert(wiphy_find_by_wdev(wdev_id),
+					&fx->work, 0, &work_ops);
+}
+
+static bool frame_xchg_match_id(const void *a, const void *b)
+{
+	const struct frame_xchg_data *fx = a;
+	const uint32_t *id = b;
+
+	return fx->work.id == *id;
 }
 
-void frame_xchg_stop(uint64_t wdev_id)
+void frame_xchg_cancel(uint32_t id)
 {
 	struct frame_xchg_data *fx =
-		l_queue_remove_if(frame_xchgs, frame_xchg_match, &wdev_id);
+		l_queue_remove_if(frame_xchgs, frame_xchg_match_id, &id);
 
 	if (!fx)
 		return;
 
-	if (fx->in_frame_cb) {
-		fx->stale = true;
-		return;
-	}
-
-	frame_xchg_reset(fx);
-	l_free(fx);
+	wiphy_radio_work_done(wiphy_find_by_wdev(fx->wdev_id), id);
 }
 
 static void frame_xchg_mlme_notify(struct l_genl_msg *msg, void *user_data)
@@ -1341,13 +1333,20 @@ error:
 	return -EIO;
 }
 
+static void destroy_xchg_data(void *user_data)
+{
+	struct frame_xchg_data *fx = user_data;
+
+	frame_xchg_destroy(&fx->work);
+}
+
 static void frame_xchg_exit(void)
 {
 	struct l_queue *groups = watch_groups;
 	struct l_queue *xchgs = frame_xchgs;
 
 	frame_xchgs = NULL;
-	l_queue_destroy(xchgs, frame_xchg_destroy);
+	l_queue_destroy(xchgs, destroy_xchg_data);
 
 	watch_groups = NULL;
 	l_queue_destroy(groups, frame_watch_group_destroy);
diff --git a/src/frame-xchg.h b/src/frame-xchg.h
index 55a55f73..ba4dc61a 100644
--- a/src/frame-xchg.h
+++ b/src/frame-xchg.h
@@ -43,14 +43,14 @@ bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type,
 bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group);
 bool frame_watch_wdev_remove(uint64_t wdev_id);
 
-void frame_xchg_start(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
+uint32_t frame_xchg_start(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 			unsigned int retry_interval, unsigned int resp_timeout,
 			unsigned int retries_on_ack, uint32_t group_id,
 			frame_xchg_cb_t cb, void *user_data,
 			frame_xchg_destroy_func_t destroy, ...);
-void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
+uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 			unsigned int retry_interval, unsigned int resp_timeout,
 			unsigned int retries_on_ack, uint32_t group_id,
 			frame_xchg_cb_t cb, void *user_data,
 			frame_xchg_destroy_func_t destroy, va_list resp_args);
-void frame_xchg_stop(uint64_t wdev_id);
+void frame_xchg_cancel(uint32_t id);
diff --git a/src/p2p.c b/src/p2p.c
index 1c9cb081..b8c299a3 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -68,6 +68,7 @@ struct p2p_device {
 	uint32_t start_stop_cmd_id;
 	l_dbus_property_complete_cb_t pending_complete;
 	struct l_dbus_message *pending_message;
+	uint32_t xchg_id;
 
 	uint8_t listen_country[3];
 	uint8_t listen_oper_class;
@@ -383,7 +384,11 @@ static void p2p_connection_reset(struct p2p_device *dev)
 	netdev_watch_remove(dev->conn_netdev_watch_id);
 
 	frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_CONNECT);
-	frame_xchg_stop(dev->wdev_id);
+
+	if (dev->xchg_id) {
+		frame_xchg_cancel(dev->xchg_id);
+		dev->xchg_id = 0;
+	}
 
 	if (!dev->enabled || (dev->enabled && dev->start_stop_cmd_id)) {
 		/*
@@ -458,9 +463,10 @@ static void p2p_peer_frame_xchg(struct p2p_peer *peer, struct iovec *tx_body,
 		peer->bss->frequency;
 
 	va_start(args, cb);
-	frame_xchg_startv(dev->wdev_id, frame, freq, retry_interval,
-				resp_timeout, retries_on_ack, group_id,
-				cb, dev, NULL, args);
+
+	dev->xchg_id = frame_xchg_startv(dev->wdev_id, frame, freq,
+				retry_interval, resp_timeout, retries_on_ack,
+				group_id, cb, dev, NULL, args);
 	va_end(args);
 
 	l_free(frame);
@@ -1175,11 +1181,17 @@ static void p2p_go_negotiation_resp_done(int error, void *user_data)
 	else
 		l_error("No GO Negotiation Confirmation frame received");
 
+	dev->xchg_id = 0;
+
 	p2p_connect_failed(dev);
 }
 
 static void p2p_go_negotiation_resp_err_done(int error, void *user_data)
 {
+	struct p2p_device *dev = user_data;
+
+	dev->xchg_id = 0;
+
 	if (error)
 		l_error("Sending the GO Negotiation Response failed: %s (%i)",
 			strerror(-error), -error);
@@ -1272,6 +1284,8 @@ static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu,
 
 	l_debug("");
 
+	dev->xchg_id = 0;
+
 	if (body_len < 8) {
 		l_error("GO Negotiation Confirmation frame too short");
 		p2p_connect_failed(dev);
-- 
2.21.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 3/6] anqp: refactor to use frame-xchg
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
  2020-07-09  0:04 ` [PATCH v3 2/6] frame-xchg: refactor to use wiphy work queue James Prestwood
@ 2020-07-09  0:04 ` James Prestwood
  2020-07-09  0:04 ` [PATCH v3 4/6] scan: refactor to use wiphy radio work queue James Prestwood
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 17431 bytes --]

The new frame-xchg module now handles a lot of what ANQP used to do. ANQP
now does not need to depend on nl80211/netdev for building and sending
frames. It also no longer needs any of the request lookups, frame watches
or to maintain a queue of requests because frame-xchg filters this for us.

From an API perspective:
 - anqp_request() was changed to take the wdev_id rather than ifindex.
 - anqp_cancel() was added so that station can properly clean up ANQP
   requests if the device disappears.

During testing a bug was also fixed in station on the timeout path
where the request queue would get popped twice.
---
 src/anqp.c    | 443 +++++++++++---------------------------------------
 src/anqp.h    |   3 +-
 src/station.c |  19 +--
 3 files changed, 103 insertions(+), 362 deletions(-)

diff --git a/src/anqp.c b/src/anqp.c
index 6627bd81..3b597835 100644
--- a/src/anqp.c
+++ b/src/anqp.c
@@ -31,105 +31,61 @@
 #include "src/module.h"
 #include "src/anqp.h"
 #include "src/util.h"
-#include "src/eap-private.h"
 #include "src/ie.h"
-#include "src/nl80211util.h"
-#include "src/nl80211cmd.h"
 #include "src/scan.h"
-#include "src/netdev.h"
 #include "src/iwd.h"
 #include "src/mpdu.h"
-#include "src/wiphy.h"
+#include "src/frame-xchg.h"
 
 #include "linux/nl80211.h"
 
+#define ANQP_GROUP	0
+
 struct anqp_request {
-	uint32_t ifindex;
+	uint64_t wdev_id;
 	anqp_response_func_t anqp_cb;
 	anqp_destroy_func_t anqp_destroy;
 	void *anqp_data;
-	uint64_t anqp_cookie;
 	uint8_t anqp_token;
+	uint32_t frequency;
+	uint8_t *frame;
+	size_t frame_len;
+	uint32_t id;
 };
 
-static struct l_genl_family *nl80211 = NULL;
-
-static struct l_queue *anqp_requests;
 static uint8_t anqp_token = 0;
 
-static uint32_t netdev_watch;
-static uint32_t unicast_watch;
-
 static void anqp_destroy(void *user_data)
 {
 	struct anqp_request *request = user_data;
 
 	if (request->anqp_destroy)
 		request->anqp_destroy(request->anqp_data);
-}
-
-static void netdev_gas_request_cb(struct l_genl_msg *msg, void *user_data)
-{
-	struct anqp_request *request = user_data;
-
-	if (l_genl_msg_get_error(msg) != 0)
-		goto error;
-
-	if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &request->anqp_cookie,
-					NL80211_ATTR_UNSPEC) < 0)
-		goto error;
-
-	return;
-
-error:
-	l_debug("Error sending CMD_FRAME (%d)", l_genl_msg_get_error(msg));
-
-	if (request->anqp_cb)
-		request->anqp_cb(ANQP_FAILED, NULL, 0, request->anqp_data);
-
-	if (request->anqp_destroy)
-		request->anqp_destroy(request->anqp_data);
 
+	l_free(request->frame);
 	l_free(request);
 }
 
-static bool match_token(const void *a, const void *b)
-{
-	const struct anqp_request *request = a;
-	const struct token_match {
-		uint32_t ifindex;
-		uint8_t token;
-
-	} *match = b;
-
-	if (request->ifindex != match->ifindex)
-		return false;
-
-	if (request->anqp_token != match->token)
-		return false;
-
-	return true;
-}
-
-static void anqp_response_frame_event(uint32_t ifindex,
-					const struct mmpdu_header *hdr,
-					const void *body, size_t body_len)
+/*
+ * By using frame-xchg we should get called back here for any frame matching our
+ * prefix until the duration expires. If frame-xchg is never signalled 'done'
+ * (returning true) we should get a timeout in anqp_frame_timeout. This is
+ * why we drop any improperly formatted frames without cleaning up the request.
+ */
+static bool anqp_response_frame_event(const struct mmpdu_header *hdr,
+					const void *body, size_t body_len,
+					int rssi, void *user_data)
 {
-	struct anqp_request *request;
+	struct anqp_request *request = user_data;
 	const uint8_t *ptr = body;
 	uint16_t status_code;
 	uint16_t delay;
 	uint16_t qrlen;
 	uint8_t adv_proto_len;
 	uint8_t token;
-	struct token_match {
-		uint32_t ifindex;
-		uint8_t token;
-
-	} match;
 
 	if (body_len < 9)
-		return;
+		return false;
 
 	/* Skip past category/action since this frame was prefixed matched */
 	ptr += 2;
@@ -138,12 +94,8 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	/* dialog token */
 	token = *ptr++;
 
-	match.ifindex = ifindex;
-	match.token = token;
-
-	request = l_queue_find(anqp_requests, match_token, &match);
-	if (!request)
-		return;
+	if (request->anqp_token != token)
+		return false;
 
 	status_code = l_get_le16(ptr);
 	ptr += 2;
@@ -151,7 +103,7 @@ static void anqp_response_frame_event(uint32_t ifindex,
 
 	if (status_code != 0) {
 		l_error("Bad status code on GAS response %u", status_code);
-		return;
+		return false;
 	}
 
 	delay = l_get_le16(ptr);
@@ -166,12 +118,12 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	 */
 	if (delay != 0) {
 		l_error("GAS comeback delay was not zero");
-		return;
+		return false;
 	}
 
 	if (*ptr != IE_TYPE_ADVERTISEMENT_PROTOCOL) {
 		l_error("GAS request not advertisement protocol");
-		return;
+		return false;
 	}
 
 	ptr++;
@@ -181,339 +133,128 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	body_len--;
 
 	if (body_len < adv_proto_len)
-		return;
+		return false;
 
 	ptr += adv_proto_len;
 	body_len -= adv_proto_len;
 
 	if (body_len < 2)
-		return;
+		return false;
 
 	qrlen = l_get_le16(ptr);
 	ptr += 2;
 
 	if (body_len < qrlen)
-		return;
-
-	l_queue_remove(anqp_requests, request);
+		return false;
 
 	l_debug("ANQP response received from "MAC, MAC_STR(hdr->address_2));
 
 	if (request->anqp_cb)
-		request->anqp_cb(ANQP_SUCCESS, ptr, qrlen,
-					request->anqp_data);
-
-	if (request->anqp_destroy)
-		request->anqp_destroy(request->anqp_data);
+		request->anqp_cb(ANQP_SUCCESS, ptr, qrlen, request->anqp_data);
 
-	l_free(request);
+	anqp_destroy(request);
 
-	return;
+	return true;
 }
 
-static void netdev_gas_timeout_cb(void *user_data)
+static const struct frame_xchg_prefix anqp_frame_prefix = {
+	.data = (uint8_t []) {
+		0x04, 0x0b,
+	},
+	.len = 2,
+};
+
+static void anqp_frame_timeout(int error, void *user_data)
 {
 	struct anqp_request *request = user_data;
+	enum anqp_result result = ANQP_TIMEOUT;
 
-	l_debug("GAS request timed out");
+	if (error < 0) {
+		result = ANQP_FAILED;
+		l_error("Sending ANQP request failed: %s (%i)",
+			strerror(-error), -error);
+	}
 
 	if (request->anqp_cb)
-		request->anqp_cb(ANQP_TIMEOUT, NULL, 0,
-					request->anqp_data);
+		request->anqp_cb(result, NULL, 0, request->anqp_data);
 
-	/* allows anqp_request to be re-entrant */
 	if (request->anqp_destroy)
 		request->anqp_destroy(request->anqp_data);
 
-	l_queue_remove(anqp_requests, request);
-	l_free(request);
+	anqp_destroy(request);
 }
 
-static bool match_cookie(const void *a, const void *b)
+static uint8_t *anqp_build_frame(const uint8_t *addr, struct scan_bss *bss,
+					const uint8_t *anqp, size_t len,
+					size_t *len_out)
 {
-	const struct anqp_request *request = a;
-	const struct cookie_match {
-		uint64_t cookie;
-		uint32_t ifindex;
-	} *match = b;
+	uint8_t *frame = l_malloc(len + 33);
+	uint8_t *ptr;
 
-	if (match->ifindex != request->ifindex)
-		return false;
+	memset(frame, 0, len + 33);
 
-	if (match->cookie != request->anqp_cookie)
-		return false;
+	l_put_le16(0x00d0, frame + 0);
+	memcpy(frame + 4, bss->addr, 6);
+	memcpy(frame + 10, addr, 6);
+	memcpy(frame + 16, bss->addr, 6);
 
-	return true;
-}
+	ptr = frame + 24;
 
-static void anqp_frame_wait_cancel_event(struct l_genl_msg *msg,
-						uint32_t ifindex)
-{
-	uint64_t cookie;
-	struct anqp_request *request;
-	struct cookie_match {
-		uint64_t cookie;
-		uint32_t ifindex;
-	} match;
+	*ptr++ = 0x04;			/* Category: Public */
+	*ptr++ = 0x0a;			/* Action: GAS initial Request */
+	*ptr++ = anqp_token++;		/* Dialog Token */
+	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
+	*ptr++ = 2;
 
-	l_debug("");
-
-	if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &cookie,
-					NL80211_ATTR_UNSPEC) < 0)
-		return;
-
-	match.cookie = cookie;
-	match.ifindex = ifindex;
+	*ptr++ = 0x7f;
+	*ptr++ = IE_ADVERTISEMENT_ANQP;
+	l_put_le16(len, ptr);
+	ptr += 2;
 
-	request = l_queue_find(anqp_requests, match_cookie, &match);
-	if (!request)
-		return;
+	memcpy(ptr, anqp, len);
+	ptr += len;
 
-	if (cookie != request->anqp_cookie)
-		return;
+	*len_out = ptr - frame;
 
-	netdev_gas_timeout_cb(request);
+	return frame;
 }
 
-uint32_t anqp_request(uint32_t ifindex, const uint8_t *addr,
-				struct scan_bss *bss, const uint8_t *anqp,
-				size_t len, anqp_response_func_t cb,
-				void *user_data, anqp_destroy_func_t destroy)
+uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
+			struct scan_bss *bss, const uint8_t *anqp,
+			size_t len, anqp_response_func_t cb,
+			void *user_data, anqp_destroy_func_t destroy)
 {
 	struct anqp_request *request;
-	uint8_t frame[512];
-	struct l_genl_msg *msg;
 	struct iovec iov[2];
-	uint32_t id;
-	uint32_t duration = 300;
-	struct netdev *netdev = netdev_find(ifindex);
-
-	if (!netdev)
-		return 0;
-
-	/*
-	 * TODO: Netdev dependencies will eventually be removed so we need
-	 * another way to figure out wiphy capabilities.
-	 */
-	if (!wiphy_can_offchannel_tx(netdev_get_wiphy(netdev))) {
-		l_error("ANQP failed, driver does not support offchannel TX");
-		return 0;
-	}
-
-	frame[0] = 0x04;		/* Category: Public */
-	frame[1] = 0x0a;		/* Action: GAS initial Request */
-	frame[2] = anqp_token;		/* Dialog Token */
-	frame[3] = IE_TYPE_ADVERTISEMENT_PROTOCOL;
-	frame[4] = 2;
-	frame[5] = 0x7f;
-	frame[6] = IE_ADVERTISEMENT_ANQP;
-	l_put_le16(len, frame + 7);
-
-	iov[0].iov_base = frame;
-	iov[0].iov_len = 9;
-	iov[1].iov_base = (void *)anqp;
-	iov[1].iov_len = len;
 
 	request = l_new(struct anqp_request, 1);
 
-	request->ifindex = ifindex;
+	request->wdev_id = wdev_id;
+	request->frequency = bss->frequency;
 	request->anqp_cb = cb;
 	request->anqp_destroy = destroy;
-	request->anqp_token = anqp_token++;
+	request->anqp_token = anqp_token;
 	request->anqp_data = user_data;
 
-	msg = nl80211_build_cmd_frame(ifindex, addr, bss->addr,
-					bss->frequency, iov, 2);
-
-	l_genl_msg_append_attr(msg, NL80211_ATTR_OFFCHANNEL_TX_OK, 0, "");
-	l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &duration);
-
-	id = l_genl_family_send(nl80211, msg, netdev_gas_request_cb,
-					request, NULL);
-
-	if (!id) {
-		l_debug("Failed to send ANQP request");
-		l_genl_msg_unref(msg);
-		l_free(request);
-		return 0;
-	}
-
-	l_debug("ANQP request sent to "MAC, MAC_STR(bss->addr));
-
-	l_queue_push_head(anqp_requests, request);
-
-	return id;
-}
-
-static void netdev_frame_cb(struct l_genl_msg *msg, void *user_data)
-{
-	if (l_genl_msg_get_error(msg) < 0)
-		l_error("Could not register frame watch type %04x: %i",
-			L_PTR_TO_UINT(user_data), l_genl_msg_get_error(msg));
-}
-
-static void anqp_register_frame(uint32_t ifindex)
-{
-	struct l_genl_msg *msg;
-	uint16_t frame_type = 0x00d0;
-	uint8_t prefix[] = { 0x04, 0x0b };
-
-	msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 34);
-
-	l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
-	l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_TYPE, 2, &frame_type);
-	l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
-					sizeof(prefix), prefix);
-
-	l_genl_family_send(nl80211, msg, netdev_frame_cb,
-			L_UINT_TO_PTR(frame_type), NULL);
-}
-
-static void anqp_netdev_watch(struct netdev *netdev,
-				enum netdev_watch_event event, void *user_data)
-{
-	switch (event) {
-	case NETDEV_WATCH_EVENT_NEW:
-		if (netdev_get_iftype(netdev) == NETDEV_IFTYPE_STATION)
-			anqp_register_frame(netdev_get_ifindex(netdev));
-
-		return;
-	default:
-		break;
-	}
-}
-
-static void anqp_unicast_notify(struct l_genl_msg *msg, void *user_data)
-{
-	const struct mmpdu_header *mpdu = NULL;
-	const uint8_t *body;
-	struct l_genl_attr attr;
-	uint16_t type, len;
-	uint16_t frame_len = 0;
-	const void *data;
-	uint8_t cmd;
-	uint32_t ifindex = 0;
-
-	if (l_queue_isempty(anqp_requests))
-		return;
-
-	cmd = l_genl_msg_get_command(msg);
-	if (!cmd)
-		return;
-
-	if (!l_genl_attr_init(&attr, msg))
-		return;
-
-	while (l_genl_attr_next(&attr, &type, &len, &data)) {
-		switch (type) {
-		case NL80211_ATTR_IFINDEX:
-			if (len != sizeof(uint32_t)) {
-				l_warn("Invalid interface index attribute");
-				return;
-			}
-
-			ifindex = *((uint32_t *) data);
-
-			break;
-		case NL80211_ATTR_FRAME:
-			if (mpdu)
-				return;
-
-			mpdu = mpdu_validate(data, len);
-			if (!mpdu)
-				l_error("Frame didn't validate as MMPDU");
-
-			frame_len = len;
-			break;
-		}
-	}
-
-	if (!ifindex || !mpdu)
-		return;
-
-	body = mmpdu_body(mpdu);
-
-	anqp_response_frame_event(ifindex, mpdu, body,
-				(const uint8_t *) mpdu + frame_len - body);
-}
-
-static void anqp_mlme_notify(struct l_genl_msg *msg, void *user_data)
-{
-	struct l_genl_attr attr;
-	uint16_t type, len;
-	const void *data;
-	uint8_t cmd;
-	uint32_t ifindex = 0;
-
-	if (l_queue_isempty(anqp_requests))
-		return;
-
-	cmd = l_genl_msg_get_command(msg);
-
-	l_debug("MLME notification %s(%u)", nl80211cmd_to_string(cmd), cmd);
-
-	if (!l_genl_attr_init(&attr, msg))
-		return;
-
-	while (l_genl_attr_next(&attr, &type, &len, &data)) {
-		switch (type) {
-		case NL80211_ATTR_IFINDEX:
-			if (len != sizeof(uint32_t)) {
-				l_warn("Invalid interface index attribute");
-				return;
-			}
-
-			ifindex = *((uint32_t *) data);
-			break;
-		}
-	}
+	request->frame = anqp_build_frame(addr, bss, anqp, len,
+						&request->frame_len);
 
-	if (!ifindex) {
-		l_warn("MLME notification is missing ifindex attribute");
-		return;
-	}
-
-	switch (cmd) {
-	case NL80211_CMD_FRAME_WAIT_CANCEL:
-		anqp_frame_wait_cancel_event(msg, ifindex);
-		break;
-	}
-}
-
-static int anqp_init(void)
-{
-	struct l_genl *genl = iwd_get_genl();
-
-	nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
-
-	anqp_requests = l_queue_new();
+	iov[0].iov_base = request->frame;
+	iov[0].iov_len = request->frame_len;
+	iov[1].iov_base = NULL;
 
-	netdev_watch =  netdev_watch_add(anqp_netdev_watch, NULL, NULL);
+	l_debug("Sending ANQP request");
 
-	unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
-						anqp_unicast_notify,
-						NULL, NULL);
+	request->id = frame_xchg_start(request->wdev_id, iov,
+				request->frequency, 0, 300, 0,
+				ANQP_GROUP, anqp_frame_timeout, request, NULL,
+				&anqp_frame_prefix, anqp_response_frame_event,
+				NULL);
 
-	if (!l_genl_family_register(nl80211, "mlme", anqp_mlme_notify,
-								NULL, NULL))
-		l_error("Registering for MLME notification failed");
-
-	return 0;
+	return true;
 }
 
-static void anqp_exit(void)
+void anqp_cancel(uint32_t id)
 {
-	struct l_genl *genl = iwd_get_genl();
-
-	l_genl_family_free(nl80211);
-	nl80211 = NULL;
-
-	l_queue_destroy(anqp_requests, anqp_destroy);
-
-	netdev_watch_remove(netdev_watch);
-
-	l_genl_remove_unicast_watch(genl, unicast_watch);
+	frame_xchg_cancel(id);
 }
-
-IWD_MODULE(anqp, anqp_init, anqp_exit);
-IWD_MODULE_DEPENDS(anqp, netdev);
diff --git a/src/anqp.h b/src/anqp.h
index 998277dd..4d96c9ec 100644
--- a/src/anqp.h
+++ b/src/anqp.h
@@ -34,7 +34,8 @@ typedef void (*anqp_response_func_t)(enum anqp_result result,
 					const void *anqp, size_t len,
 					void *user_data);
 
-uint32_t anqp_request(uint32_t ifindex, const uint8_t *addr,
+uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
 			struct scan_bss *bss, const uint8_t *anqp, size_t len,
 			anqp_response_func_t cb, void *user_data,
 			anqp_destroy_func_t destroy);
+void anqp_cancel(uint32_t id);
diff --git a/src/station.c b/src/station.c
index 3719e0b9..a19195f4 100644
--- a/src/station.c
+++ b/src/station.c
@@ -447,6 +447,9 @@ static void remove_anqp(void *data)
 {
 	struct anqp_entry *entry = data;
 
+	if (entry->pending)
+		anqp_cancel(entry->pending);
+
 	l_free(entry);
 }
 
@@ -476,12 +479,9 @@ static void station_anqp_response_cb(enum anqp_result result,
 	char **realms = NULL;
 	struct nai_search search;
 
-	entry->pending = 0;
-
 	l_debug("");
 
-	if (result == ANQP_TIMEOUT) {
-		l_queue_remove(station->anqp_pending, entry);
+	if (result != ANQP_SUCCESS) {
 		/* TODO: try next BSS */
 		goto request_done;
 	}
@@ -583,11 +583,10 @@ static bool station_start_anqp(struct station *station, struct network *network,
 	 * these are checked in hs20_find_settings_file.
 	 */
 
-	entry->pending = anqp_request(netdev_get_ifindex(station->netdev),
-					netdev_get_address(station->netdev),
-					bss, anqp, ptr - anqp,
-					station_anqp_response_cb,
-					entry, l_free);
+	entry->pending = anqp_request(netdev_get_wdev_id(station->netdev),
+				netdev_get_address(station->netdev), bss, anqp,
+				ptr - anqp, station_anqp_response_cb,
+				entry, NULL);
 	if (!entry->pending) {
 		l_free(entry);
 		return false;
@@ -3252,7 +3251,7 @@ static void station_free(struct station *station)
 
 	watchlist_destroy(&station->state_watches);
 
-	l_queue_destroy(station->anqp_pending, l_free);
+	l_queue_destroy(station->anqp_pending, remove_anqp);
 
 	l_free(station);
 }
-- 
2.21.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 4/6] scan: refactor to use wiphy radio work queue
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
  2020-07-09  0:04 ` [PATCH v3 2/6] frame-xchg: refactor to use wiphy work queue James Prestwood
  2020-07-09  0:04 ` [PATCH v3 3/6] anqp: refactor to use frame-xchg James Prestwood
@ 2020-07-09  0:04 ` James Prestwood
  2020-07-09  0:04 ` [PATCH v3 5/6] station: cancel hidden network scan when connecting James Prestwood
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 10731 bytes --]

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    | 130 +++++++++++++++-----------------------------------
 src/scan.h    |   3 --
 src/station.c |  16 +------
 3 files changed, 40 insertions(+), 109 deletions(-)

v3:
 - Added back in calling destroy() immediately if an already triggered
   scan was canceled. This is safe since destroy() gets NULL'ed so
   once the scan results come back in the work will get signaled as
   done but destroy() wont get called twice.

diff --git a/src/scan.c b/src/scan.c
index 83804e57..52217b7a 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,6 @@ struct scan_context {
 	bool triggered:1;
 	/* Whether any commands from current request's queue have started */
 	bool started:1;
-	bool suspended:1;
 	struct wiphy *wiphy;
 };
 
@@ -118,7 +119,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 +135,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 +161,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 +182,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 +224,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 +525,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 +545,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 +618,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 +645,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 +828,23 @@ 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);
-
-	if (sc->suspended)
-		return true;
-
-	if (sc->state != SCAN_STATE_NOT_RUNNING)
-		return true;
-
-	if (sc->start_cmd_id || sc->get_scan_cmd_id)
-		return true;
-
-	while (sr) {
-		if (!scan_request_send_trigger(sc, sr))
-			return true;
+	struct scan_request *sr = l_container_of(item,
+						struct scan_request, work);
+	struct scan_context *sc = sr->sc;
 
-		scan_request_failed(sc, sr, -EIO);
+	if (!scan_request_send_trigger(sc, sr))
+		return 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 +1496,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);
 
@@ -1679,7 +1662,7 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
 
 		/* Send the next command of a new or an ongoing request */
 		if (send_next)
-			start_next_scan_request(sc);
+			start_next_scan_request(&sr->work);
 
 		if (!get_results)
 			break;
@@ -1722,15 +1705,6 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
 			sc->triggered = 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);
 		}
 
 		break;
@@ -2133,32 +2107,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 a19195f4..d7b319d8 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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 5/6] station: cancel hidden network scan when connecting
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
                   ` (2 preceding siblings ...)
  2020-07-09  0:04 ` [PATCH v3 4/6] scan: refactor to use wiphy radio work queue James Prestwood
@ 2020-07-09  0:04 ` James Prestwood
  2020-07-09  0:04 ` [PATCH v3 6/6] netdev: use wiphy radio work queue for connections James Prestwood
  2020-07-09 15:46 ` [PATCH v3 1/6] wiphy: introduce new radio management APIs Denis Kenzior
  5 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1611 bytes --]

Before connecting to a hidden network we must scan. During this scan
if another connection attempt comes in the expected behavior is to
abort the original connection. Rather than waiting for the scan to
complete, then canceling the original hidden connection we can just
cancel the hidden scan immediately, reply to dbus, and continue with
the new connection attempt.
---
 src/station.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/station.c b/src/station.c
index d7b319d8..35f4a96d 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2474,6 +2474,19 @@ void station_connect_network(struct station *station, struct network *network,
 	struct l_dbus *dbus = dbus_get_bus();
 	int err;
 
+	/*
+	 * If a hidden scan is not completed, station_is_busy would not
+	 * indicate anything is going on so we need to cancel the scan and
+	 * fail the connection now.
+	 */
+	if (station->hidden_network_scan_id) {
+		scan_cancel(netdev_get_wdev_id(station->netdev),
+				station->hidden_network_scan_id);
+
+		dbus_pending_reply(&station->hidden_pending,
+				dbus_error_failed(station->hidden_pending));
+	}
+
 	if (station_is_busy(station)) {
 		station_disconnect_onconnect(station, network, bss, message);
 
@@ -2525,6 +2538,8 @@ static bool station_hidden_network_scan_results(int err,
 
 	msg = station->hidden_pending;
 	station->hidden_pending = NULL;
+	/* Zero this now so station_connect_network knows the scan is done */
+	station->hidden_network_scan_id = 0;
 
 	if (err) {
 		dbus_pending_reply(&msg, dbus_error_failed(msg));
-- 
2.21.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 6/6] netdev: use wiphy radio work queue for connections
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
                   ` (3 preceding siblings ...)
  2020-07-09  0:04 ` [PATCH v3 5/6] station: cancel hidden network scan when connecting James Prestwood
@ 2020-07-09  0:04 ` James Prestwood
  2020-07-09 15:46 ` [PATCH v3 1/6] wiphy: introduce new radio management APIs Denis Kenzior
  5 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2020-07-09  0:04 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 5738 bytes --]

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 | 86 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 66 insertions(+), 20 deletions(-)

v3:
 - Added check for netdev->work.id before signaling work as done. This is
   because AP/AdHoc both use some netdev APIs and these modules do not yet
   use the work queue.

diff --git a/src/netdev.c b/src/netdev.c
index e469fb1c..a7f92ff8 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);
 
@@ -625,12 +632,12 @@ static void netdev_free(void *data)
 		netdev->mac_change_cmd_id = 0;
 	}
 
+	scan_wdev_remove(netdev->wdev_id);
+
 	if (netdev->events_ready)
 		WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
 					netdev, NETDEV_WATCH_EVENT_DEL);
 
-	scan_wdev_remove(netdev->wdev_id);
-
 	watchlist_destroy(&netdev->station_watches);
 
 	l_io_destroy(netdev->pae_io);
@@ -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)
@@ -2426,25 +2439,60 @@ struct rtnl_data {
 	int ref;
 };
 
-static int netdev_begin_connection(struct netdev *netdev,
-					struct l_genl_msg *cmd_connect)
+static bool netdev_connection_ready(struct wiphy_radio_work_item *item)
 {
-	if (cmd_connect) {
+	struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+	if (netdev->connect_cmd) {
 		netdev->connect_cmd_id = l_genl_family_send(nl80211,
-					cmd_connect, netdev_cmd_connect_cb,
-					netdev, NULL);
+					netdev->connect_cmd,
+					netdev_cmd_connect_cb, netdev, NULL);
 
 		if (!netdev->connect_cmd_id) {
-			l_genl_msg_unref(cmd_connect);
-			return -EIO;
+			l_genl_msg_unref(netdev->connect_cmd);
+			netdev->connect_cmd = NULL;
+
+			netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+				MMPDU_STATUS_CODE_UNSPECIFIED);
+			return true;
 		}
+
+		netdev->connect_cmd = NULL;
 	}
 
-	handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+	if (netdev->ap) {
+		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
+		 */
 
-	/* set connected since the auth protocols cannot do so internally */
-	if (netdev->ap && auth_proto_start(netdev->ap))
 		netdev->connected = true;
+	}
+
+	return false;
+}
+
+static const struct wiphy_radio_work_item_ops connect_work_ops = {
+	.do_work = netdev_connection_ready,
+};
+
+static int netdev_begin_connection(struct netdev *netdev,
+					struct l_genl_msg *cmd_connect)
+{
+	netdev->connect_cmd = cmd_connect;
+
+	wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+				&connect_work_ops);
 
 	return 0;
 }
@@ -2514,6 +2562,8 @@ static void netdev_mac_power_up_cb(int error, uint16_t type,
 		return;
 	}
 
+	handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+
 	/*
 	 * Pick up where we left off in netdev_connect_commmon.
 	 */
@@ -2649,6 +2699,8 @@ static int netdev_connect_common(struct netdev *netdev,
 			return ret;
 	}
 
+	handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
+
 	return netdev_begin_connection(netdev, cmd_connect);
 }
 
@@ -3092,7 +3144,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 +3224,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,
+				&connect_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,
-- 
2.21.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 1/6] wiphy: introduce new radio management APIs
  2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
                   ` (4 preceding siblings ...)
  2020-07-09  0:04 ` [PATCH v3 6/6] netdev: use wiphy radio work queue for connections James Prestwood
@ 2020-07-09 15:46 ` Denis Kenzior
  5 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2020-07-09 15:46 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1979 bytes --]

Hi James,

On 7/8/20 7:04 PM, James Prestwood wrote:
> 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.
> 

Patches 1-3, 5 applied now, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-07-09 15:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-09  0:04 [PATCH v3 1/6] wiphy: introduce new radio management APIs James Prestwood
2020-07-09  0:04 ` [PATCH v3 2/6] frame-xchg: refactor to use wiphy work queue James Prestwood
2020-07-09  0:04 ` [PATCH v3 3/6] anqp: refactor to use frame-xchg James Prestwood
2020-07-09  0:04 ` [PATCH v3 4/6] scan: refactor to use wiphy radio work queue James Prestwood
2020-07-09  0:04 ` [PATCH v3 5/6] station: cancel hidden network scan when connecting James Prestwood
2020-07-09  0:04 ` [PATCH v3 6/6] netdev: use wiphy radio work queue for connections James Prestwood
2020-07-09 15:46 ` [PATCH v3 1/6] wiphy: introduce new radio management APIs Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.