[gnome-remote-desktop] rdp: Add class for detecting network characteristics
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-remote-desktop] rdp: Add class for detecting network characteristics
- Date: Fri, 3 Sep 2021 09:34:42 +0000 (UTC)
commit 4c646d45a77598345ab10fee863a733a34d83c8d
Author: Pascal Nowack <Pascal Nowack gmx de>
Date: Mon May 31 14:42:34 2021 +0200
rdp: Add class for detecting network characteristics
Starting with Windows 8, Microsoft added a few PDUs, that allow the
server to detect network characteristics, such as the round trip time
(RTT) or the available bandwidth.
These characteristics are measured with the RTT
Measure-Request/-Response and Bandwidth Measure-Start/-Stop PDUs.
In order to make use of these PDUs, add a new class that hooks up to
these PDUs.
Currently, only RTT detection is implemented.
RTT detection works by putting a sequence number to a hash table,
saving the time for the sequence, sending an RTTRequest to the client
with the sequence number and when the client responds with the
RTTResponse, calculate the time difference between the response and the
request.
This RTT value will then be forwarded to a consumer, like the graphics
pipeline.
The graphics pipeline will forward the RTT value to the GFX surfaces,
which will then use that value to calculate the activate threshold for
the throttling mechanism.
To smooth out spikes in the RTT value, ignore out-of-order RTTResponses
and calculate the average RTT value from the RTTs of the last 500ms.
Also limit the maximum RTT value to 1000ms, as RTTs above that value
are extremely hard to handle, if they can be handled.
The ping interval will for now always be 70ms and the RTTRequsts will
only be sent, when there is an RTT consumer.
src/grd-rdp-network-autodetection.c | 337 ++++++++++++++++++++++++++++++++++++
src/grd-rdp-network-autodetection.h | 46 +++++
src/grd-rdp-private.h | 2 +
src/grd-types.h | 1 +
src/meson.build | 2 +
5 files changed, 388 insertions(+)
---
diff --git a/src/grd-rdp-network-autodetection.c b/src/grd-rdp-network-autodetection.c
new file mode 100644
index 0000000..d79dcff
--- /dev/null
+++ b/src/grd-rdp-network-autodetection.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2021 Pascal Nowack
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "grd-rdp-network-autodetection.h"
+
+#include "grd-rdp-graphics-pipeline.h"
+#include "grd-rdp-private.h"
+
+#define BW_MEASURE_SEQUENCE_NUMBER 0
+#define PING_INTERVAL_MS 70
+#define RTT_AVG_PERIOD_US (500 * 1000)
+
+typedef struct _PingInfo
+{
+ uint16_t sequence_number;
+ int64_t ping_time_us;
+} PingInfo;
+
+typedef struct _RTTInfo
+{
+ int64_t round_trip_time_us;
+ int64_t response_time_us;
+} RTTInfo;
+
+struct _GrdRdpNetworkAutodetection
+{
+ GObject parent;
+
+ rdpContext *rdp_context;
+ rdpAutoDetect *rdp_autodetect;
+ GMutex shutdown_mutex;
+ gboolean in_shutdown;
+
+ GMutex consumer_mutex;
+ GrdRdpNwAutodetectRTTConsumer rtt_consumers;
+
+ GMutex sequence_mutex;
+ GHashTable *sequences;
+
+ GSource *ping_source;
+ GQueue *pings;
+ GQueue *round_trip_times;
+
+ uint16_t next_sequence_number;
+};
+
+G_DEFINE_TYPE (GrdRdpNetworkAutodetection, grd_rdp_network_autodetection, G_TYPE_OBJECT);
+
+void
+grd_rdp_network_autodetection_invoke_shutdown (GrdRdpNetworkAutodetection *network_autodetection)
+{
+ g_mutex_lock (&network_autodetection->shutdown_mutex);
+ network_autodetection->in_shutdown = TRUE;
+ g_mutex_unlock (&network_autodetection->shutdown_mutex);
+}
+
+static uint16_t
+get_next_free_sequence_number (GrdRdpNetworkAutodetection *network_autodetection)
+{
+ uint16_t sequence_number = network_autodetection->next_sequence_number;
+
+ while (sequence_number == BW_MEASURE_SEQUENCE_NUMBER ||
+ g_hash_table_contains (network_autodetection->sequences,
+ GUINT_TO_POINTER (sequence_number)))
+ ++sequence_number;
+
+ network_autodetection->next_sequence_number = sequence_number + 1;
+
+ return sequence_number;
+}
+
+static gboolean
+emit_ping (gpointer user_data)
+{
+ GrdRdpNetworkAutodetection *network_autodetection = user_data;
+ rdpAutoDetect *rdp_autodetect = network_autodetection->rdp_autodetect;
+ PingInfo *ping_info;
+
+ ping_info = g_malloc0 (sizeof (PingInfo));
+
+ g_mutex_lock (&network_autodetection->sequence_mutex);
+ ping_info->sequence_number = get_next_free_sequence_number (network_autodetection);
+ ping_info->ping_time_us = g_get_monotonic_time ();
+
+ g_hash_table_add (network_autodetection->sequences,
+ GUINT_TO_POINTER (ping_info->sequence_number));
+ g_queue_push_tail (network_autodetection->pings, ping_info);
+ g_mutex_unlock (&network_autodetection->sequence_mutex);
+
+ rdp_autodetect->RTTMeasureRequest (network_autodetection->rdp_context,
+ ping_info->sequence_number);
+
+ return G_SOURCE_CONTINUE;
+}
+
+void
+grd_rdp_network_autodetection_ensure_rtt_consumer (GrdRdpNetworkAutodetection *network_autodetection,
+ GrdRdpNwAutodetectRTTConsumer rtt_consumer)
+{
+ g_assert (rtt_consumer != GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_NONE);
+
+ g_mutex_lock (&network_autodetection->consumer_mutex);
+ network_autodetection->rtt_consumers |= rtt_consumer;
+
+ if (!network_autodetection->ping_source)
+ {
+ emit_ping (network_autodetection);
+
+ network_autodetection->ping_source = g_timeout_source_new (PING_INTERVAL_MS);
+ g_source_set_callback (network_autodetection->ping_source, emit_ping,
+ network_autodetection, NULL);
+ g_source_attach (network_autodetection->ping_source, NULL);
+ }
+ g_mutex_unlock (&network_autodetection->consumer_mutex);
+}
+
+void
+grd_rdp_network_autodetection_remove_rtt_consumer (GrdRdpNetworkAutodetection *network_autodetection,
+ GrdRdpNwAutodetectRTTConsumer rtt_consumer)
+{
+ g_mutex_lock (&network_autodetection->consumer_mutex);
+ network_autodetection->rtt_consumers &= ~rtt_consumer;
+
+ if (network_autodetection->rtt_consumers == GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_NONE &&
+ network_autodetection->ping_source)
+ {
+ g_source_destroy (network_autodetection->ping_source);
+ g_clear_pointer (&network_autodetection->ping_source, g_source_unref);
+ }
+ g_mutex_unlock (&network_autodetection->consumer_mutex);
+}
+
+static gboolean
+has_rtt_consumer (GrdRdpNetworkAutodetection *network_autodetection,
+ GrdRdpNwAutodetectRTTConsumer rtt_consumer)
+{
+ g_assert (!g_mutex_trylock (&network_autodetection->consumer_mutex));
+
+ return !!(network_autodetection->rtt_consumers & rtt_consumer);
+}
+
+static void
+track_round_trip_time (GrdRdpNetworkAutodetection *network_autodetection,
+ int64_t ping_time_us,
+ int64_t pong_time_us)
+{
+ RTTInfo *rtt_info;
+
+ rtt_info = g_malloc0 (sizeof (RTTInfo));
+ rtt_info->round_trip_time_us = MIN (pong_time_us - ping_time_us, G_USEC_PER_SEC);
+ rtt_info->response_time_us = pong_time_us;
+
+ g_queue_push_tail (network_autodetection->round_trip_times, rtt_info);
+}
+
+static void
+remove_old_round_trip_times (GrdRdpNetworkAutodetection *network_autodetection)
+{
+ int64_t current_time_us;
+ RTTInfo *rtt_info;
+
+ current_time_us = g_get_monotonic_time ();
+ while ((rtt_info = g_queue_peek_head (network_autodetection->round_trip_times)) &&
+ current_time_us - rtt_info->response_time_us >= RTT_AVG_PERIOD_US)
+ g_free (g_queue_pop_head (network_autodetection->round_trip_times));
+}
+
+static int64_t
+get_current_avg_round_trip_time_us (GrdRdpNetworkAutodetection *network_autodetection)
+{
+ int64_t sum_round_trip_times_us = 0;
+ uint32_t total_round_trip_times;
+ RTTInfo *rtt_info;
+ GQueue *tmp;
+
+ remove_old_round_trip_times (network_autodetection);
+ if (!g_queue_get_length (network_autodetection->round_trip_times))
+ return 0;
+
+ tmp = g_queue_copy (network_autodetection->round_trip_times);
+ total_round_trip_times = g_queue_get_length (tmp);
+
+ while ((rtt_info = g_queue_pop_head (tmp)))
+ sum_round_trip_times_us += rtt_info->round_trip_time_us;
+
+ g_queue_free (tmp);
+
+ return sum_round_trip_times_us / total_round_trip_times;
+}
+
+static BOOL
+autodetect_rtt_measure_response (rdpContext *rdp_context,
+ uint16_t sequence_number)
+{
+ RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
+ GrdRdpNetworkAutodetection *network_autodetection;
+ PingInfo *ping_info;
+ int64_t pong_time_us;
+ int64_t avg_round_trip_time_us;
+ gboolean has_rtt_consumer_rdpgfx = FALSE;
+
+ network_autodetection = rdp_peer_context->network_autodetection;
+
+ pong_time_us = g_get_monotonic_time ();
+
+ g_mutex_lock (&network_autodetection->sequence_mutex);
+ if (!g_hash_table_contains (network_autodetection->sequences,
+ GUINT_TO_POINTER (sequence_number)))
+ {
+ g_mutex_unlock (&network_autodetection->sequence_mutex);
+ return TRUE;
+ }
+
+ while ((ping_info = g_queue_pop_head (network_autodetection->pings)) &&
+ ping_info->sequence_number != sequence_number)
+ {
+ g_hash_table_remove (network_autodetection->sequences,
+ GUINT_TO_POINTER (ping_info->sequence_number));
+ g_clear_pointer (&ping_info, g_free);
+ }
+
+ if (ping_info)
+ {
+ int64_t ping_time_us = ping_info->ping_time_us;
+
+ g_assert (ping_info->sequence_number == sequence_number);
+
+ track_round_trip_time (network_autodetection, ping_time_us, pong_time_us);
+ avg_round_trip_time_us =
+ get_current_avg_round_trip_time_us (network_autodetection);
+
+ g_hash_table_remove (network_autodetection->sequences,
+ GUINT_TO_POINTER (ping_info->sequence_number));
+ }
+ g_mutex_unlock (&network_autodetection->sequence_mutex);
+
+ g_mutex_lock (&network_autodetection->consumer_mutex);
+ has_rtt_consumer_rdpgfx = has_rtt_consumer (
+ network_autodetection, GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX);
+ g_mutex_unlock (&network_autodetection->consumer_mutex);
+
+ g_mutex_lock (&network_autodetection->shutdown_mutex);
+ if (ping_info && !network_autodetection->in_shutdown && has_rtt_consumer_rdpgfx)
+ {
+ grd_rdp_graphics_pipeline_notify_new_round_trip_time (
+ rdp_peer_context->graphics_pipeline, avg_round_trip_time_us);
+ }
+ g_mutex_unlock (&network_autodetection->shutdown_mutex);
+
+ g_free (ping_info);
+
+ return TRUE;
+}
+
+GrdRdpNetworkAutodetection *
+grd_rdp_network_autodetection_new (rdpContext *rdp_context)
+{
+ GrdRdpNetworkAutodetection *network_autodetection;
+ rdpAutoDetect *rdp_autodetect = rdp_context->autodetect;
+
+ network_autodetection = g_object_new (GRD_TYPE_RDP_NETWORK_AUTODETECTION,
+ NULL);
+
+ network_autodetection->rdp_context = rdp_context;
+ network_autodetection->rdp_autodetect = rdp_autodetect;
+
+ rdp_autodetect->RTTMeasureResponse = autodetect_rtt_measure_response;
+
+ return network_autodetection;
+}
+
+static void
+grd_rdp_network_autodetection_dispose (GObject *object)
+{
+ GrdRdpNetworkAutodetection *network_autodetection =
+ GRD_RDP_NETWORK_AUTODETECTION (object);
+
+ if (network_autodetection->ping_source)
+ {
+ g_source_destroy (network_autodetection->ping_source);
+ g_clear_pointer (&network_autodetection->ping_source, g_source_unref);
+ }
+
+ if (network_autodetection->round_trip_times)
+ {
+ g_queue_free_full (network_autodetection->round_trip_times, g_free);
+ network_autodetection->round_trip_times = NULL;
+ }
+
+ if (network_autodetection->pings)
+ {
+ g_queue_free_full (network_autodetection->pings, g_free);
+ network_autodetection->pings = NULL;
+ }
+
+ g_clear_pointer (&network_autodetection->sequences, g_hash_table_destroy);
+
+ G_OBJECT_CLASS (grd_rdp_network_autodetection_parent_class)->dispose (object);
+}
+
+static void
+grd_rdp_network_autodetection_init (GrdRdpNetworkAutodetection *network_autodetection)
+{
+ network_autodetection->sequences = g_hash_table_new (NULL, NULL);
+ network_autodetection->pings = g_queue_new ();
+ network_autodetection->round_trip_times = g_queue_new ();
+
+ g_mutex_init (&network_autodetection->shutdown_mutex);
+ g_mutex_init (&network_autodetection->consumer_mutex);
+ g_mutex_init (&network_autodetection->sequence_mutex);
+}
+
+static void
+grd_rdp_network_autodetection_class_init (GrdRdpNetworkAutodetectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = grd_rdp_network_autodetection_dispose;
+}
diff --git a/src/grd-rdp-network-autodetection.h b/src/grd-rdp-network-autodetection.h
new file mode 100644
index 0000000..73d1ad8
--- /dev/null
+++ b/src/grd-rdp-network-autodetection.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 Pascal Nowack
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GRD_RDP_NETWORK_AUTODETECTION_H
+#define GRD_RDP_NETWORK_AUTODETECTION_H
+
+#include <freerdp/freerdp.h>
+#include <glib-object.h>
+
+#define GRD_TYPE_RDP_NETWORK_AUTODETECTION (grd_rdp_network_autodetection_get_type ())
+G_DECLARE_FINAL_TYPE (GrdRdpNetworkAutodetection, grd_rdp_network_autodetection,
+ GRD, RDP_NETWORK_AUTODETECTION, GObject);
+
+typedef enum _GrdRdpNwAutodetectRTTConsumer
+{
+ GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_NONE = 0,
+ GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX = 1 << 0,
+} GrdRdpNwAutodetectRTTConsumer;
+
+GrdRdpNetworkAutodetection *grd_rdp_network_autodetection_new (rdpContext *rdp_context);
+
+void grd_rdp_network_autodetection_invoke_shutdown (GrdRdpNetworkAutodetection *network_autodetection);
+
+void grd_rdp_network_autodetection_ensure_rtt_consumer (GrdRdpNetworkAutodetection *network_autodetection,
+ GrdRdpNwAutodetectRTTConsumer rtt_consumer);
+
+void grd_rdp_network_autodetection_remove_rtt_consumer (GrdRdpNetworkAutodetection *network_autodetection,
+ GrdRdpNwAutodetectRTTConsumer rtt_consumer);
+
+#endif /* GRD_RDP_NETWORK_AUTODETECTION_H */
diff --git a/src/grd-rdp-private.h b/src/grd-rdp-private.h
index afb0c6f..eebc2ce 100644
--- a/src/grd-rdp-private.h
+++ b/src/grd-rdp-private.h
@@ -36,6 +36,8 @@ typedef struct _RdpPeerContext
RFX_CONTEXT *rfx_context;
wStream *encode_stream;
+ GrdRdpNetworkAutodetection *network_autodetection;
+
/* Virtual Channel Manager */
HANDLE vcm;
diff --git a/src/grd-types.h b/src/grd-types.h
index b004d08..edea7f0 100644
--- a/src/grd-types.h
+++ b/src/grd-types.h
@@ -31,6 +31,7 @@ typedef struct _GrdRdpEventQueue GrdRdpEventQueue;
typedef struct _GrdRdpGfxFrameLog GrdRdpGfxFrameLog;
typedef struct _GrdRdpGfxSurface GrdRdpGfxSurface;
typedef struct _GrdRdpGraphicsPipeline GrdRdpGraphicsPipeline;
+typedef struct _GrdRdpNetworkAutodetection GrdRdpNetworkAutodetection;
typedef struct _GrdRdpSAMFile GrdRdpSAMFile;
typedef struct _GrdRdpServer GrdRdpServer;
typedef struct _GrdRdpSurface GrdRdpSurface;
diff --git a/src/meson.build b/src/meson.build
index e9bcc9f..ef29d7b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -48,6 +48,8 @@ if have_rdp
'grd-rdp-gfx-surface.h',
'grd-rdp-graphics-pipeline.c',
'grd-rdp-graphics-pipeline.h',
+ 'grd-rdp-network-autodetection.c',
+ 'grd-rdp-network-autodetection.h',
'grd-rdp-pipewire-stream.c',
'grd-rdp-pipewire-stream.h',
'grd-rdp-private.h',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]