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

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

On Wed, Jul 15, 2026 at 4:59=E2=80=AFAM 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)
>       =3D -1 EAGAIN (Resource temporarily unavailable)
>   ppoll([{fd=3D7, events=3DPOLLOUT}], 1, {tv_sec=3D0, tv_nsec=3D0}) =3D 0=
 (Timeout)
>
> fd=3D7 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=3D'/' 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=3D7, bluetoothd's GMainLoop stalls
>      and can no longer send D-Bus replies or signals.
>
> Fix
> ---
> Three related changes:
>
> 1. Remove the broad type=3D'signal',sender=3D<svc>,path_namespace=3D<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=3D
>    '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/20260710075548.1072741-1-xiuzhuo.shang@o=
ss.qualcomm.com/
>
>  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, co=
nst char *path,
>                 return;
>         }
>
> +       if (client->proxy_filter) {
> +               DBusMessageIter copy =3D *iter;
> +
> +               if (!client->proxy_filter(client, path, interface,
> +                                       &copy, client->filter_user_data))
> +                       return;
> +       }
> +
>         proxy =3D proxy_new(client, path, interface);
>         if (proxy =3D=3D 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 =3D g_strdup(root_path);
>         client->connected =3D FALSE;
>
> -       client->match_rules =3D g_ptr_array_sized_new(1);
> -       g_ptr_array_set_free_func(client->match_rules, g_free);
> -
>         client->watch =3D 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=3D'sig=
nal',"
> -                               "sender=3D'%s',path_namespace=3D'%s'",
> -                               client->service_name, client->base_path))=
;
> -
> -       for (i =3D 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 =3D=3D NULL)
>                 return;
>
> @@ -1297,13 +1292,6 @@ void g_dbus_client_unref(GDBusClient *client)
>                 dbus_pending_call_unref(client->get_objects_call);
>         }
>
> -       for (i =3D 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(GDBusCli=
ent *client,
>
>         return TRUE;
>  }
> +
> +gboolean g_dbus_client_set_proxy_filter(GDBusClient *client,
> +                                       GDBusProxyFilterFunction proxy_fi=
lter,
> +                                       void *user_data)
> +{
> +       if (client =3D=3D NULL)
> +               return FALSE;
> +
> +       client->proxy_filter =3D proxy_filter;
> +       client->filter_user_data =3D 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 *c=
lient, void *user_data);
>  typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data)=
;
>  typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *n=
ame,
>                                         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_ch=
anged,
>                                         void *user_data);
> +gboolean g_dbus_client_set_proxy_filter(GDBusClient *client,
> +                                       GDBusProxyFilterFunction proxy_fi=
lter,
> +                                       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 *use=
r_data)
>         device_changed(proxy, path);
>  }
>
> +static gboolean proxy_filter(GDBusClient *client, const char *path,
> +                               const char *interface, DBusMessageIter *i=
ter,
> +                               void *user_data)
> +{
> +       DBusMessageIter props, entry;
> +
> +       if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) =3D=3D FALSE)
> +               return TRUE;
> +
> +       if (dbus_message_iter_get_arg_type(iter) !=3D DBUS_TYPE_ARRAY)
> +               return TRUE;
> +
> +       dbus_message_iter_recurse(iter, &props);
> +
> +       while (dbus_message_iter_get_arg_type(&props) =3D=3D DBUS_TYPE_DI=
CT_ENTRY) {
> +               const char *key;
> +
> +               dbus_message_iter_recurse(&props, &entry);
> +               dbus_message_iter_get_basic(&entry, &key);
> +
> +               if (g_str_equal(key, "AddressType") =3D=3D 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=3Drandom??
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
>


--=20
Luiz Augusto von Dentz