diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index db823177e636..aae337572be7 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -520,6 +520,54 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) qrtr_node_assign(node, le32_to_cpu(pkt->server.node)); } + /* The DSPs are star-connected through this node: forward packets + * destined to another node onto that node's endpoint (e.g. the + * modem's voice stack talking to the ADSP's audio service). Only + * DATA and RESUME_TX transit; control packets keep going to the + * local ns, which does its own mesh-wide redistribution. RESUME_TX + * additionally releases this hop's flow-control token on the + * arrival link: per-hop counters advance in lockstep with the + * end-to-end ones since every packet of the flow transits here. + */ + if (cb->dst_node != qrtr_local_nid && + cb->dst_node != QRTR_NODE_BCAST && + (cb->type == QRTR_TYPE_DATA || cb->type == QRTR_TYPE_RESUME_TX)) { + struct sockaddr_qrtr from = {AF_QIPCRTR, + cb->src_node, cb->src_port}; + struct sockaddr_qrtr to = {AF_QIPCRTR, + cb->dst_node, cb->dst_port}; + struct qrtr_node *dst; + + dst = qrtr_node_lookup(cb->dst_node); + if (!dst || dst == node) { + if (dst) + qrtr_node_release(dst); + goto err; + } + + if (cb->type == QRTR_TYPE_RESUME_TX) { + struct sk_buff *clone; + + clone = skb_clone(skb, GFP_ATOMIC); + if (clone) + qrtr_tx_resume(node, clone); + } + + pr_debug("qrtr: fwd %u:%u -> %u:%u type %d len %zu\n", + cb->src_node, cb->src_port, + cb->dst_node, cb->dst_port, cb->type, size); + + if (skb_cow_head(skb, sizeof(struct qrtr_hdr_v1))) { + qrtr_node_release(dst); + goto err; + } + + qrtr_node_enqueue(dst, skb, cb->type, &from, &to); + qrtr_node_release(dst); + + return 0; + } + if (cb->type == QRTR_TYPE_RESUME_TX) { qrtr_tx_resume(node, skb); } else {