diff mbox series

[v4,5/8] station: add Affinities DBus property

Message ID 20240823222339.328006-5-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [v4,1/8] doc: Document station Affinities property | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-gitlint success GitLint

Commit Message

James Prestwood Aug. 23, 2024, 10:23 p.m. UTC
This property will hold an array of object paths for
BasicServiceSet (BSS) objects. For the purpose of this patch
only the setter/getter and client watch is implemented. The
purpose of this array is to guide or loosly lock IWD to certain
BSS's provided that some external client has more information
about the environment than what IWD takes into account for its
roaming decisions.

For the time being, the array is limited to only the connected
BSS path, and any roams or disconnects will clear the array.

The intended use case for this is if the device is stationary
an external client could reduce the likelihood of roaming by
setting the affinity to the current BSS.
---
 src/station.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)

Comments

Denis Kenzior Aug. 28, 2024, 2:57 a.m. UTC | #1
Hi James,

On 8/23/24 5:23 PM, James Prestwood wrote:
> This property will hold an array of object paths for
> BasicServiceSet (BSS) objects. For the purpose of this patch
> only the setter/getter and client watch is implemented. The
> purpose of this array is to guide or loosly lock IWD to certain

typo: 'loosely'

> BSS's provided that some external client has more information
> about the environment than what IWD takes into account for its
> roaming decisions.
> 
> For the time being, the array is limited to only the connected
> BSS path, and any roams or disconnects will clear the array.
> 
> The intended use case for this is if the device is stationary
> an external client could reduce the likelihood of roaming by
> setting the affinity to the current BSS.
> ---
>   src/station.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 145 insertions(+)
> 
> diff --git a/src/station.c b/src/station.c
> index 30a1232a..c72d42ae 100644
> --- a/src/station.c
> +++ b/src/station.c
> @@ -128,6 +128,10 @@ struct station {
>   
>   	uint64_t last_roam_scan;
>   
> +	struct l_queue *affinities;
> +	unsigned int affinity_watch;
> +	char *affinity_client;
> +
>   	bool preparing_roam : 1;
>   	bool roam_scan_full : 1;
>   	bool signal_low : 1;
> @@ -449,6 +453,14 @@ static const char *station_get_bss_path(struct station *station,
>   	return __network_path_append_bss(network_path, bss);
>   }
>   
> +static bool match_bss_path(const void *data, const void *user_data)
> +{
> +	const char *path1 = data;
> +	const char *path2 = user_data;
> +
> +	return !strcmp(path1, path2);
> +}
> +
>   static bool station_unregister_bss(struct station *station,
>   					struct scan_bss *bss)
>   {
> @@ -457,6 +469,8 @@ static bool station_unregister_bss(struct station *station,
>   	if (L_WARN_ON(!path))
>   		return false;
>   
> +	l_queue_remove_if(station->affinities, match_bss_path, path);
> +
>   	return l_dbus_unregister_object(dbus_get_bus(), path);
>   }
>   
> @@ -1740,6 +1754,13 @@ static void station_enter_state(struct station *station,
>   		station_set_evict_nocarrier(station, true);
>   		station_set_drop_neighbor_discovery(station, false);
>   		station_set_drop_unicast_l2_multicast(station, false);
> +
> +		if (station->affinity_watch) {
> +			l_dbus_remove_watch(dbus_get_bus(),
> +						station->affinity_watch);
> +			station->affinity_watch = 0;
> +		}
> +

Hmm, but not resetting affinities?

>   		break;
>   	case STATION_STATE_DISCONNECTING:
>   	case STATION_STATE_NETCONFIG:
> @@ -1747,6 +1768,12 @@ static void station_enter_state(struct station *station,
>   	case STATION_STATE_ROAMING:
>   	case STATION_STATE_FT_ROAMING:
>   	case STATION_STATE_FW_ROAMING:
> +		if (station->affinity_watch) {
> +			l_dbus_remove_watch(dbus_get_bus(),
> +						station->affinity_watch);
> +			station->affinity_watch = 0;

Okay.  This reminds me that for hardware supporting FW roaming we may want to 
return NotCapable when setting Affinities.

> +		}
> +
>   		station_set_evict_nocarrier(station, false);
>   		break;
>   	}

<snip>

> +static void station_affinity_watch_destroy(void *user_data)
> +{
> +	struct station *station = user_data;
> +
> +	l_free(station->affinity_client);
> +	station->affinity_client = NULL;
> +
> +	l_queue_destroy(station->affinities, l_free);
> +	station->affinities = NULL;
> +
> +	l_dbus_property_changed(dbus_get_bus(),
> +				netdev_get_path(station->netdev),
> +				IWD_STATION_INTERFACE, "Affinities");

What if the list was empty?

> +}
> +
> +static struct l_dbus_message *station_property_set_affinities(
> +					struct l_dbus *dbus,
> +					struct l_dbus_message *message,
> +					struct l_dbus_message_iter *new_value,
> +					l_dbus_property_complete_cb_t complete,
> +					void *user_data)
> +{
> +	struct station *station = user_data;
> +	struct l_dbus_message_iter array;
> +	const char *sender = l_dbus_message_get_sender(message);
> +	const char *path;
> +	struct l_queue *new_affinities;
> +
> +	if (!station->connected_network)
> +		return dbus_error_not_connected(message);
> +
> +	if (!station->affinity_watch) {
> +		station->affinity_client = l_strdup(sender);
> +		station->affinity_watch = l_dbus_add_disconnect_watch(dbus,
> +					sender,
> +					station_affinity_disconnected_cb,
> +					station,
> +					station_affinity_watch_destroy);
> +	} else if (strcmp(station->affinity_client, sender)) {
> +		l_warn("Only one client may manage Affinities property");
> +		return dbus_error_not_available(message);

Hmm, we probably need a new error for 'permission denied or something'

> +	}
> +
> +	if (!l_dbus_message_iter_get_variant(new_value, "ao", &array))
> +		return dbus_error_invalid_args(message);

So what happens to affinity_client / affinity_watch above?

> +
> +	new_affinities = l_queue_new();
> +
> +	while (l_dbus_message_iter_next_entry(&array, &path)) {
> +		struct scan_bss *bss = l_dbus_object_get_data(dbus_get_bus(),
> +						path,
> +						IWD_BSS_INTERFACE);
> +
> +		/*
> +		 * TODO: For now only allow the affinities array to contain the
> +		 *       connected BSS path. Any other values will be rejected.
> +		 *       This could change in the future.
> +		 */
> +		if (bss != station->connected_bss) {
> +			l_queue_destroy(new_affinities, l_free);
> +			return dbus_error_invalid_args(message);

same as above?

You may want to make your life easier by just looking for an empty list or list 
of size 1 with the connected bss.

> +		}
> +
> +		l_queue_push_head(new_affinities, l_strdup(path));
> +	}
> +
> +	l_queue_destroy(station->affinities, l_free);
> +	station->affinities = new_affinities;

If the list is empty, should you unregister the watch?

> +
> +	l_dbus_property_changed(dbus, netdev_get_path(station->netdev),
> +				IWD_STATION_INTERFACE, "Affinities");
> +
> +	complete(dbus, message, NULL);

It is the other way around, we send the 'message_return' first, then the signal. 
  Do you want to spuriously emit if the two lists are the same?

> +
> +	return NULL;
> +}
> +
>   void station_foreach(station_foreach_func_t func, void *user_data)
>   {
>   	const struct l_queue_entry *entry;

Regards,
-Denis
diff mbox series

Patch

diff --git a/src/station.c b/src/station.c
index 30a1232a..c72d42ae 100644
--- a/src/station.c
+++ b/src/station.c
@@ -128,6 +128,10 @@  struct station {
 
 	uint64_t last_roam_scan;
 
+	struct l_queue *affinities;
+	unsigned int affinity_watch;
+	char *affinity_client;
+
 	bool preparing_roam : 1;
 	bool roam_scan_full : 1;
 	bool signal_low : 1;
@@ -449,6 +453,14 @@  static const char *station_get_bss_path(struct station *station,
 	return __network_path_append_bss(network_path, bss);
 }
 
+static bool match_bss_path(const void *data, const void *user_data)
+{
+	const char *path1 = data;
+	const char *path2 = user_data;
+
+	return !strcmp(path1, path2);
+}
+
 static bool station_unregister_bss(struct station *station,
 					struct scan_bss *bss)
 {
@@ -457,6 +469,8 @@  static bool station_unregister_bss(struct station *station,
 	if (L_WARN_ON(!path))
 		return false;
 
+	l_queue_remove_if(station->affinities, match_bss_path, path);
+
 	return l_dbus_unregister_object(dbus_get_bus(), path);
 }
 
@@ -1740,6 +1754,13 @@  static void station_enter_state(struct station *station,
 		station_set_evict_nocarrier(station, true);
 		station_set_drop_neighbor_discovery(station, false);
 		station_set_drop_unicast_l2_multicast(station, false);
+
+		if (station->affinity_watch) {
+			l_dbus_remove_watch(dbus_get_bus(),
+						station->affinity_watch);
+			station->affinity_watch = 0;
+		}
+
 		break;
 	case STATION_STATE_DISCONNECTING:
 	case STATION_STATE_NETCONFIG:
@@ -1747,6 +1768,12 @@  static void station_enter_state(struct station *station,
 	case STATION_STATE_ROAMING:
 	case STATION_STATE_FT_ROAMING:
 	case STATION_STATE_FW_ROAMING:
+		if (station->affinity_watch) {
+			l_dbus_remove_watch(dbus_get_bus(),
+						station->affinity_watch);
+			station->affinity_watch = 0;
+		}
+
 		station_set_evict_nocarrier(station, false);
 		break;
 	}
@@ -4520,6 +4547,118 @@  static bool station_property_get_state(struct l_dbus *dbus,
 	return true;
 }
 
+static bool station_property_get_affinities(struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_builder *builder,
+					void *user_data)
+{
+	struct station *station = user_data;
+	const struct l_queue_entry *e;
+
+	if (!station->connected_network)
+		return false;
+
+	l_dbus_message_builder_enter_array(builder, "o");
+
+	for (e = l_queue_get_entries(station->affinities); e; e = e->next) {
+		const char *path = e->data;
+
+		l_dbus_message_builder_append_basic(builder, 'o', path);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
+static void station_affinity_disconnected_cb(struct l_dbus *dbus,
+						void *user_data)
+{
+	struct station *station = user_data;
+
+	l_dbus_remove_watch(dbus_get_bus(), station->affinity_watch);
+	station->affinity_watch = 0;
+
+	l_debug("client that set affinity has disconnected");
+}
+
+static void station_affinity_watch_destroy(void *user_data)
+{
+	struct station *station = user_data;
+
+	l_free(station->affinity_client);
+	station->affinity_client = NULL;
+
+	l_queue_destroy(station->affinities, l_free);
+	station->affinities = NULL;
+
+	l_dbus_property_changed(dbus_get_bus(),
+				netdev_get_path(station->netdev),
+				IWD_STATION_INTERFACE, "Affinities");
+}
+
+static struct l_dbus_message *station_property_set_affinities(
+					struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_iter *new_value,
+					l_dbus_property_complete_cb_t complete,
+					void *user_data)
+{
+	struct station *station = user_data;
+	struct l_dbus_message_iter array;
+	const char *sender = l_dbus_message_get_sender(message);
+	const char *path;
+	struct l_queue *new_affinities;
+
+	if (!station->connected_network)
+		return dbus_error_not_connected(message);
+
+	if (!station->affinity_watch) {
+		station->affinity_client = l_strdup(sender);
+		station->affinity_watch = l_dbus_add_disconnect_watch(dbus,
+					sender,
+					station_affinity_disconnected_cb,
+					station,
+					station_affinity_watch_destroy);
+	} else if (strcmp(station->affinity_client, sender)) {
+		l_warn("Only one client may manage Affinities property");
+		return dbus_error_not_available(message);
+	}
+
+	if (!l_dbus_message_iter_get_variant(new_value, "ao", &array))
+		return dbus_error_invalid_args(message);
+
+	new_affinities = l_queue_new();
+
+	while (l_dbus_message_iter_next_entry(&array, &path)) {
+		struct scan_bss *bss = l_dbus_object_get_data(dbus_get_bus(),
+						path,
+						IWD_BSS_INTERFACE);
+
+		/*
+		 * TODO: For now only allow the affinities array to contain the
+		 *       connected BSS path. Any other values will be rejected.
+		 *       This could change in the future.
+		 */
+		if (bss != station->connected_bss) {
+			l_queue_destroy(new_affinities, l_free);
+			return dbus_error_invalid_args(message);
+		}
+
+		l_queue_push_head(new_affinities, l_strdup(path));
+	}
+
+	l_queue_destroy(station->affinities, l_free);
+	station->affinities = new_affinities;
+
+	l_dbus_property_changed(dbus, netdev_get_path(station->netdev),
+				IWD_STATION_INTERFACE, "Affinities");
+
+	complete(dbus, message, NULL);
+
+	return NULL;
+}
+
 void station_foreach(station_foreach_func_t func, void *user_data)
 {
 	const struct l_queue_entry *entry;
@@ -4842,6 +4981,9 @@  static void station_free(struct station *station)
 
 	l_queue_destroy(station->roam_bss_list, l_free);
 
+	if (station->affinity_watch)
+		l_dbus_remove_watch(dbus_get_bus(), station->affinity_watch);
+
 	l_free(station);
 }
 
@@ -4878,6 +5020,9 @@  static void station_setup_interface(struct l_dbus_interface *interface)
 					station_property_get_scanning, NULL);
 	l_dbus_interface_property(interface, "State", 0, "s",
 					station_property_get_state, NULL);
+	l_dbus_interface_property(interface, "Affinities", 0, "ao",
+					station_property_get_affinities,
+					station_property_set_affinities);
 }
 
 static void station_destroy_interface(void *user_data)