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, > + ©, 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