diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index b3f9bbcf9ab9..47a66db76cf1 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -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 */