net: qrtr: ns: push adsp service announcements to the modem (FP6 voice)

The DSP firmwares expect the downstream IPC-router flooding model: a
node never sends NEW_LOOKUP for a service hosted on a peer node and
relies on the application processor pushing every NEW_SERVER/DEL_SERVER
to every link. Mainline's name service only broadcasts local servers;
remote servers reach other remote nodes solely via persistent lookups
nobody in the firmware issues.

Observed on the FP6 (SM7635): at boot the modem queries the in-kernel
pd-mapper for 'avs/audio' and receives 'msm/adsp/audio_pd' instance 74,
but never contacts the ADSP's service-registry notifier (svc 66 inst 74,
node 5 port 2) because it was never told that service exists - zero
forwarded modem->adsp packets across a full modem boot with the
forwarding path instrumented. Without the avs/audio-up notification the
modem's Voice Services never creates a vocoder session (empty
placeholder encoder/decoder during live VoLTE calls AND CVD loopback,
journal/calls.md sessions 15/16).

A first version of this patch flooded every remote announcement to every
hello'd node, downstream-style; it wedged the phone within ~20 s of the
DSPs booting, recurring in waves (control packets are exempt from qrtr
flow control, so any echo between routers runs unbounded, and DSP
crash/recovery cycles re-trigger it). Push only the one edge voice
needs instead: ADSP-hosted servers, to the modem, both live (new/del)
and replayed when the modem says HELLO. Any echo is structurally
loop-free: a re-announcement arriving from the modem is pushed to
nobody (target == origin). Pairs with the data-path forwarding carry
0c63174949.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
This commit is contained in:
Jorijn van der Graaf 2026-07-12 17:41:49 +02:00
commit fb5953a877

View file

@ -70,6 +70,7 @@ struct qrtr_node {
unsigned int id;
struct xarray servers;
u32 server_count;
bool hello;
};
/* Max nodes, server, lookup limits are chosen based on the current platform
@ -212,6 +213,60 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
pr_err("failed to send lookup notification\n");
}
/* FP6 voice bring-up: the DSP firmwares expect the downstream IPC-router
* flooding model - they never send NEW_LOOKUP for services on their peers
* and instead rely on the application processor pushing announcements to
* every link. The modem's voice stack locates the ADSP's service-registry
* notifier this way; without it Voice Services never sees avs/audio come
* up and never creates a vocoder session.
*
* Flooding every announcement to every node wedges the phone within
* seconds of the DSPs booting (control packets are exempt from qrtr flow
* control, so any echo between routers runs unbounded). Push exactly the
* one edge voice needs: ADSP-hosted servers, to the modem, only. Any echo
* is structurally loop-free: a re-announcement of an ADSP server arriving
* from the modem is pushed to nobody (target == origin).
*/
#define QRTR_NS_FWD_SRC_NODE 5 /* adsp */
#define QRTR_NS_FWD_DST_NODE 0 /* modem */
/* Runtime kill-switch (qrtr.fp6_ns_push): off by default so a boot is
* always calm; flip it on, then restart the modem rproc so its HELLO
* triggers the replay.
*/
static bool fp6_ns_push;
module_param(fp6_ns_push, bool, 0644);
MODULE_PARM_DESC(fp6_ns_push, "FP6: push adsp service announcements to the modem");
static void service_announce_remotes(struct qrtr_server *srv,
unsigned int origin, bool new)
{
struct sockaddr_qrtr sq;
struct qrtr_node *node;
if (!fp6_ns_push)
return;
if (srv->node != QRTR_NS_FWD_SRC_NODE)
return;
if (origin == QRTR_NS_FWD_DST_NODE ||
srv->node == QRTR_NS_FWD_DST_NODE ||
qrtr_ns.local_node == QRTR_NS_FWD_DST_NODE)
return;
node = xa_load(&nodes, QRTR_NS_FWD_DST_NODE);
if (!node || !node->hello)
return;
sq.sq_family = AF_QIPCRTR;
sq.sq_node = QRTR_NS_FWD_DST_NODE;
sq.sq_port = QRTR_PORT_CTRL;
if (new)
service_announce_new(&sq, srv);
else
service_announce_del(&sq, srv);
}
static int announce_servers(struct sockaddr_qrtr *sq)
{
struct qrtr_server *srv;
@ -234,6 +289,24 @@ static int announce_servers(struct sockaddr_qrtr *sq)
return ret;
}
}
/* FP6 voice: replay the ADSP's servers to a (re)booting modem (see
* service_announce_remotes())
*/
if (fp6_ns_push && sq->sq_node == QRTR_NS_FWD_DST_NODE) {
node = xa_load(&nodes, QRTR_NS_FWD_SRC_NODE);
if (!node)
return 0;
xa_for_each(&node->servers, index, srv) {
ret = service_announce_new(sq, srv);
if (ret < 0 && ret != -ENODEV) {
pr_err("failed to announce new service\n");
return ret;
}
}
}
return 0;
}
@ -308,6 +381,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
/* Broadcast the removal of local servers */
if (srv->node == qrtr_ns.local_node && bcast)
service_announce_del(&qrtr_ns.bcast_sq, srv);
else if (bcast)
service_announce_remotes(srv, srv->node, false);
/* Announce the service's disappearance to observers */
list_for_each(li, &qrtr_ns.lookups) {
@ -352,8 +427,13 @@ static int say_hello(struct sockaddr_qrtr *dest)
/* Announce the list of servers registered on the local node */
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
{
struct qrtr_node *node;
int ret;
node = node_get(sq->sq_node);
if (node)
node->hello = true;
ret = say_hello(sq);
if (ret < 0)
return ret;
@ -521,6 +601,8 @@ static int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
pr_err("failed to announce new service\n");
return ret;
}
} else {
service_announce_remotes(srv, from->sq_node, true);
}
/* Notify any potential lookups about the new server */