Re: [PATCH v2] gdbus: Remove broad match rule and add proxy filter support

Luiz Augusto von Dentz <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth,dev.linux.lists.ofono
Message-ID <CABBYNZ+D8BhV_-3n+JV3715CAfZcsteAD14xyVmjpvrvwouo_g@mail.gmail.com>
Hi Xiuzhuo,

On Wed, Jul 15, 2026 at 4:59 AM Xiuzhuo Shang
<[email protected]> wrote:
>
> Problem
> -------
> On embedded platforms running continuous BLE scanning, bluetoothd
> eventually stalls with its D-Bus socket to dbus-daemon full. strace
> on a hung bluetoothd shows a repeating pattern:
>
>   sendmsg(7, {org.bluez.Device1 PropertiesChanged}, MSG_NOSIGNAL)
>       = -1 EAGAIN (Resource temporarily unavailable)
>   ppoll([{fd=7, events=POLLOUT}], 1, {tv_sec=0, tv_nsec=0}) = 0 (Timeout)
>
> fd=7 never becomes writable; bluetoothd's GMainLoop remains stuck
> waiting for POLLOUT and cannot dispatch any further D-Bus events,
> making the daemon appear hung and unresponsive to commands.
>
> The backpressure chain that causes this:
>   1. BLE scanning generates high-rate PropertiesChanged(RSSI) signals
>      (~400/s with typical BLE traffic).
>   2. ofono's broad path_namespace='/' match rule causes dbus-daemon to
>      route all these signals to ofono even though ofono has no use for
>      BLE RSSI data.
>   3. ofono's single-threaded GLib loop cannot consume them fast enough;
>      undelivered messages accumulate inside dbus-daemon (457 MB
>      observed after ~3 hours of scanning).
>   4. dbus-daemon, busy draining its write queue toward ofono, stops
>      reading from bluetoothd's socket in time; bluetoothd's kernel
>      send buffer fills up and sendmsg() returns EAGAIN.
>   5. With POLLOUT registered on fd=7, bluetoothd's GMainLoop stalls
>      and can no longer send D-Bus replies or signals.
>
> Fix
> ---
> Three related changes:
>
> 1. Remove the broad type='signal',sender=<svc>,path_namespace=<path>
>    match rule from g_dbus_client_new_full(). This rule was the sole
>    feeder for the signal_func path in message_filter(). ofono never
>    calls g_dbus_client_set_signal_watch() so signal_func is always
>    NULL; the broad rule therefore served no purpose and caused
>    dbus-daemon to route every bluetoothd signal to ofono.
>
> 2. Remove the now-empty match_rules GPtrArray infrastructure
>    (field declaration, init, AddMatch loop, RemoveMatch loop, free).
>    No match rules are added to this array any more.
>
> 3. Add a generic GDBusProxyFilterFunction callback and
>    g_dbus_client_set_proxy_filter() API to GDBusClient. The filter
>    is called from parse_properties() before proxy_new(), so a FALSE
>    return prevents both proxy creation and per-device
>    PropertiesChanged watch registration. This keeps all BlueZ-
>    specific logic out of the gdbus layer.
>
>    Use this in hfp_hf_bluez5.c to skip Device1 proxies for BLE
>    random-address devices: ofono only needs BR/EDR (AddressType=
>    'public') devices for HFP/HSP. Skipping BLE proxies prevents
>    dbus-daemon from registering per-device PropertiesChanged match
>    rules for advertising peripherals and eliminates the remaining
>    RSSI signal delivery to ofono.

> Together these changes prevent dbus-daemon from routing BLE
> advertising signals to ofono, breaking the backpressure chain:
> dbus-daemon memory stops growing, its write queue drains, and
> bluetoothd's send buffer clears so that sendmsg() no longer returns
> EAGAIN and the GMainLoop stall is resolved.
>
> Signed-off-by: Xiuzhuo Shang <[email protected]>
> ---
> Changes in v2:
>  - Drop Change 1 (BLE address-type filter in parse_properties()) per
>    review feedback; BlueZ-specific logic does not belong in the gdbus
>    layer.
>  - Add generic GDBusProxyFilterFunction callback and
>    g_dbus_client_set_proxy_filter() API to GDBusClient. The filter is
>    invoked before proxy_new() so a FALSE return prevents both proxy
>    creation and per-device PropertiesChanged watch registration.
>  - Use the new filter in hfp_hf_bluez5.c to skip Device1 proxies for
>    BLE random-address devices, keeping all BlueZ-specific logic in the
>    plugin as suggested.
>  - Remove now-empty match_rules GPtrArray infrastructure (field,
>    init, AddMatch loop, RemoveMatch loop, free) and unused variables.
>  - Link to v1:
>    https://lore.kernel.org/ofono/[email protected]/
>
>  gdbus/client.c          | 45 +++++++++++++++++++++--------------------
>  gdbus/gdbus.h           |  8 ++++++++
>  plugins/hfp_hf_bluez5.c | 38 ++++++++++++++++++++++++++++++++++
>  3 files changed, 69 insertions(+), 22 deletions(-)
>
> diff --git a/gdbus/client.c b/gdbus/client.c
> index 48711ae8..fa2e75c0 100644
> --- a/gdbus/client.c
> +++ b/gdbus/client.c
> @@ -46,7 +46,6 @@ struct GDBusClient {
>         guint watch;
>         guint added_watch;
>         guint removed_watch;
> -       GPtrArray *match_rules;
>         DBusPendingCall *pending_call;
>         DBusPendingCall *get_objects_call;
>         GDBusWatchFunction connect_func;
> @@ -61,6 +60,8 @@ struct GDBusClient {
>         GDBusClientFunction ready;
>         void *ready_data;
>         GDBusPropertyFunction property_changed;
> +       GDBusProxyFilterFunction proxy_filter;
> +       void *filter_user_data;
>         void *user_data;
>         GList *proxy_list;
>  };
> @@ -943,6 +944,14 @@ static void parse_properties(GDBusClient *client, const char *path,
>                 return;
>         }
>
> +       if (client->proxy_filter) {
> +               DBusMessageIter copy = *iter;
> +
> +               if (!client->proxy_filter(client, path, interface,
> +                                       &copy, client->filter_user_data))
> +                       return;
> +       }
> +
>         proxy = proxy_new(client, path, interface);
>         if (proxy == NULL)
>                 return;
> @@ -1211,7 +1220,6 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
>                                                         const char *root_path)
>  {
>         GDBusClient *client;
> -       unsigned int i;
>
>         if (!connection || !service)
>                 return NULL;
> @@ -1232,9 +1240,6 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
>         client->root_path = g_strdup(root_path);
>         client->connected = FALSE;
>
> -       client->match_rules = g_ptr_array_sized_new(1);
> -       g_ptr_array_set_free_func(client->match_rules, g_free);
> -
>         client->watch = g_dbus_add_service_watch(connection, service,
>                                                 service_connect,
>                                                 service_disconnect,
> @@ -1255,14 +1260,6 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
>                                                 "InterfacesRemoved",
>                                                 interfaces_removed,
>                                                 client, NULL);
> -       g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
> -                               "sender='%s',path_namespace='%s'",
> -                               client->service_name, client->base_path));
> -
> -       for (i = 0; i < client->match_rules->len; i++) {
> -               modify_match(client->dbus_conn, "AddMatch",
> -                               g_ptr_array_index(client->match_rules, i));
> -       }
>
>         return g_dbus_client_ref(client);
>  }
> @@ -1279,8 +1276,6 @@ GDBusClient *g_dbus_client_ref(GDBusClient *client)
>
>  void g_dbus_client_unref(GDBusClient *client)
>  {
> -       unsigned int i;
> -
>         if (client == NULL)
>                 return;
>
> @@ -1297,13 +1292,6 @@ void g_dbus_client_unref(GDBusClient *client)
>                 dbus_pending_call_unref(client->get_objects_call);
>         }
>
> -       for (i = 0; i < client->match_rules->len; i++) {
> -               modify_match(client->dbus_conn, "RemoveMatch",
> -                               g_ptr_array_index(client->match_rules, i));
> -       }
> -
> -       g_ptr_array_free(client->match_rules, TRUE);
> -
>         dbus_connection_remove_filter(client->dbus_conn,
>                                                 message_filter, client);
>
> @@ -1396,3 +1384,16 @@ gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
>
>         return TRUE;
>  }
> +
> +gboolean g_dbus_client_set_proxy_filter(GDBusClient *client,
> +                                       GDBusProxyFilterFunction proxy_filter,
> +                                       void *user_data)
> +{
> +       if (client == NULL)
> +               return FALSE;
> +
> +       client->proxy_filter = proxy_filter;
> +       client->filter_user_data = user_data;
> +
> +       return TRUE;
> +}
> diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
> index d99c2549..cc3c4e16 100644
> --- a/gdbus/gdbus.h
> +++ b/gdbus/gdbus.h
> @@ -347,6 +347,11 @@ typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data);
>  typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data);
>  typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name,
>                                         DBusMessageIter *iter, void *user_data);
> +typedef gboolean (* GDBusProxyFilterFunction) (GDBusClient *client,
> +                                       const char *path,
> +                                       const char *interface,
> +                                       DBusMessageIter *iter,
> +                                       void *user_data);
>
>  gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
>                         GDBusPropertyFunction function, void *user_data);
> @@ -377,6 +382,9 @@ gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
>                                         GDBusProxyFunction proxy_removed,
>                                         GDBusPropertyFunction property_changed,
>                                         void *user_data);
> +gboolean g_dbus_client_set_proxy_filter(GDBusClient *client,
> +                                       GDBusProxyFilterFunction proxy_filter,
> +                                       void *user_data);
>
>  #ifdef __cplusplus
>  }
> diff --git a/plugins/hfp_hf_bluez5.c b/plugins/hfp_hf_bluez5.c
> index 5ad1674f..141dc5c4 100644
> --- a/plugins/hfp_hf_bluez5.c
> +++ b/plugins/hfp_hf_bluez5.c
> @@ -791,6 +791,43 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
>         device_changed(proxy, path);
>  }
>
> +static gboolean proxy_filter(GDBusClient *client, const char *path,
> +                               const char *interface, DBusMessageIter *iter,
> +                               void *user_data)
> +{
> +       DBusMessageIter props, entry;
> +
> +       if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) == FALSE)
> +               return TRUE;
> +
> +       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
> +               return TRUE;
> +
> +       dbus_message_iter_recurse(iter, &props);
> +
> +       while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
> +               const char *key;
> +
> +               dbus_message_iter_recurse(&props, &entry);
> +               dbus_message_iter_get_basic(&entry, &key);
> +
> +               if (g_str_equal(key, "AddressType") == TRUE) {
> +                       DBusMessageIter var;
> +                       const char *addr_type;
> +
> +                       dbus_message_iter_next(&entry);
> +                       dbus_message_iter_recurse(&entry, &var);
> +                       dbus_message_iter_get_basic(&var, &addr_type);
> +
> +                       return !g_str_equal(addr_type, "random");
> +               }
> +
> +               dbus_message_iter_next(&props);
> +       }
> +
> +       return TRUE;
> +}
> +
>  static void property_changed(GDBusProxy *proxy, const char *name,
>                                         DBusMessageIter *iter, void *user_data)
>  {
> @@ -844,6 +881,7 @@ static int hfp_init(void)
>         g_dbus_client_set_connect_watch(bluez, connect_handler, NULL);
>         g_dbus_client_set_proxy_handlers(bluez, proxy_added, NULL,
>                                                 property_changed, NULL);
> +       g_dbus_client_set_proxy_filter(bluez, proxy_filter, NULL);

I don't really follow; would this register a proxy filter and
automatically remove it on the first match of an AddressType=random??
Sounds not really useful to me, what is the difference if we don't use
set_proxy_filter above?

>
>         ofono_handsfree_audio_ref();
>
> --
> 2.43.0
>


-- 
Luiz Augusto von Dentz
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.