From 718816b95232972c3891b61a35a9a7ec986f747c Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:03:37 -0700 Subject: [PATCH 1/9] net: qrtr: ns: Change to ratelimited prints Error prints for commands that are broadcasted may spam the kernel logs if a client is misbehaving and stops reading from it's socket. Change these prints to ratelimited in order to prevent performance issues when a client socket is full. Change-Id: I5aa5fa1f693f9865a3d28f28de38f870a42fbc8d Signed-off-by: Chris Lew --- net/qrtr/ns.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index 6459024cfa44..bd6b250617e2 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -159,7 +159,7 @@ static int service_announce_del(struct sockaddr_qrtr *dest, ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); if (ret < 0 && ret != -ENODEV) - pr_err("failed to announce del service %d\n", ret); + pr_err_ratelimited("failed to announce del service %d\n", ret); return ret; } @@ -190,7 +190,8 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv, ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); if (ret < 0 && ret != -ENODEV) - pr_err("failed to send lookup notification %d\n", ret); + pr_err_ratelimited("failed to send lookup notification %d\n", + ret); } static int announce_servers(struct sockaddr_qrtr *sq) @@ -374,10 +375,10 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) msg.msg_namelen = sizeof(sq); ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); - if (ret < 0 && ret != -ENODEV) { - pr_err("failed to send bye cmd %d\n", ret); - return ret; - } + if (ret < 0 && ret != -ENODEV) + pr_err_ratelimited("send bye failed: [0x%x:0x%x] 0x%x ret: %d\n", + srv->service, srv->instance, + srv->port, ret); } return 0; @@ -442,10 +443,10 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, msg.msg_namelen = sizeof(sq); ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); - if (ret < 0 && ret != -ENODEV) { - pr_err("failed to send del client cmd %d\n", ret); - return ret; - } + if (ret < 0 && ret != -ENODEV) + pr_err_ratelimited("del client cmd failed: [0x%x:0x%x] 0x%x %d\n", + srv->service, srv->instance, + srv->port, ret); } return 0; From 1dfa161834fdaa503f8caf38173ce5f29b664b02 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:03:42 -0700 Subject: [PATCH 2/9] net: qrtr: ns: Add ipc logging to ns Log packets processed by ns using ipc logging framework. Change-Id: I08e9b8551efb4a58cf899835cfac25c1952bd4b8 Signed-off-by: Jay Jayanna Signed-off-by: Chris Lew --- net/qrtr/ns.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index bd6b250617e2..ce1eca947cc9 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "qrtr: %s(): " fmt, __func__ +#include #include #include #include @@ -19,6 +20,10 @@ #define CREATE_TRACE_POINTS #include +#define NS_LOG_PAGE_CNT 4 +static void *ns_ilc; +#define NS_INFO(x, ...) ipc_log_string(ns_ilc, x, ##__VA_ARGS__) + static DEFINE_XARRAY(nodes); static struct { @@ -117,6 +122,8 @@ static int service_announce_new(struct sockaddr_qrtr *dest, trace_qrtr_ns_service_announce_new(srv->service, srv->instance, srv->node, srv->port); + NS_INFO("%s: [0x%x:0x%x]@[0x%x:0x%x]\n", __func__, srv->service, + srv->instance, srv->node, srv->port); iv.iov_base = &pkt; iv.iov_len = sizeof(pkt); @@ -144,6 +151,9 @@ static int service_announce_del(struct sockaddr_qrtr *dest, trace_qrtr_ns_service_announce_del(srv->service, srv->instance, srv->node, srv->port); + NS_INFO("%s: [0x%x:0x%x]@[0x%x:0x%x]\n", __func__, srv->service, + srv->instance, srv->node, srv->port); + iv.iov_base = &pkt; iv.iov_len = sizeof(pkt); @@ -260,6 +270,9 @@ static struct qrtr_server *server_add(unsigned int service, trace_qrtr_ns_server_add(srv->service, srv->instance, srv->node, srv->port); + NS_INFO("%s: [0x%x:0x%x]@[0x%x:0x%x]\n", __func__, srv->service, + srv->instance, srv->node, srv->port); + return srv; err: @@ -581,6 +594,29 @@ static void ctrl_cmd_del_lookup(struct sockaddr_qrtr *from, } } +static void ns_log_msg(const struct qrtr_ctrl_pkt *pkt, + struct sockaddr_qrtr *sq) +{ + unsigned int cmd = le32_to_cpu(pkt->cmd); + + if (cmd == QRTR_TYPE_HELLO || cmd == QRTR_TYPE_BYE) + NS_INFO("cmd:0x%x node[0x%x]\n", cmd, sq->sq_node); + else if (cmd == QRTR_TYPE_DEL_CLIENT) + NS_INFO("cmd:0x%x addr[0x%x:0x%x]\n", cmd, + le32_to_cpu(pkt->client.node), + le32_to_cpu(pkt->client.port)); + else if (cmd == QRTR_TYPE_NEW_SERVER || cmd == QRTR_TYPE_DEL_SERVER) + NS_INFO("cmd:0x%x SVC[0x%x:0x%x] addr[0x%x:0x%x]\n", cmd, + le32_to_cpu(pkt->server.service), + le32_to_cpu(pkt->server.instance), + le32_to_cpu(pkt->server.node), + le32_to_cpu(pkt->server.port)); + else if (cmd == QRTR_TYPE_NEW_LOOKUP || cmd == QRTR_TYPE_DEL_LOOKUP) + NS_INFO("cmd:0x%x SVC[0x%x:0x%x]\n", cmd, + le32_to_cpu(pkt->server.service), + le32_to_cpu(pkt->server.instance)); +} + static void qrtr_ns_worker(struct kthread_work *work) { const struct qrtr_ctrl_pkt *pkt; @@ -622,6 +658,8 @@ static void qrtr_ns_worker(struct kthread_work *work) trace_qrtr_ns_message(qrtr_ctrl_pkt_strings[cmd], sq.sq_node, sq.sq_port); + ns_log_msg(pkt, &sq); + ret = 0; switch (cmd) { case QRTR_TYPE_HELLO: @@ -688,6 +726,8 @@ int qrtr_ns_init(void) kthread_init_worker(&qrtr_ns.kworker); kthread_init_work(&qrtr_ns.work, qrtr_ns_worker); + ns_ilc = ipc_log_context_create(NS_LOG_PAGE_CNT, "qrtr_ns", 0); + ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM, PF_QIPCRTR, &qrtr_ns.sock); if (ret < 0) From 405ddae2c7ebdda11ee1601471a15ed56f222579 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:03:48 -0700 Subject: [PATCH 3/9] net: qrtr: Add support to read qrtr packet size Some transports need to know qrtr packet to read complete packet from underlying transport. Add API support to get the incoming packet size form qrtr header. Change-Id: I4e4e4ab2e4c1fe0e1e1261af85a8b8618ce65bb3 Signed-off-by: Arun Kumar Neelakantam Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 42 ++++++++++++++++++++++++++++++++++++++++++ net/qrtr/qrtr.h | 1 + 2 files changed, 43 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index cd16f774bbc7..27e3785c8596 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -471,6 +471,48 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid) spin_unlock_irqrestore(&qrtr_nodes_lock, flags); } +/** + * qrtr_peek_pkt_size() - Peek into the packet header to get potential pkt size + * + * @data: Starting address of the packet which points to router header. + * + * @returns: potential packet size on success, < 0 on error. + * + * This function is used by the underlying transport abstraction layer to + * peek into the potential packet size of an incoming packet. This information + * is used to perform link layer fragmentation and re-assembly + */ +int qrtr_peek_pkt_size(const void *data) +{ + const struct qrtr_hdr_v1 *v1; + const struct qrtr_hdr_v2 *v2; + unsigned int hdrlen; + unsigned int size; + unsigned int ver; + + /* Version field in v1 is little endian, so this works for both cases */ + ver = *(u8 *)data; + + switch (ver) { + case QRTR_PROTO_VER_1: + v1 = data; + hdrlen = sizeof(*v1); + size = le32_to_cpu(v1->size); + break; + case QRTR_PROTO_VER_2: + v2 = data; + hdrlen = sizeof(*v2) + v2->optlen; + size = le32_to_cpu(v2->size); + break; + default: + pr_err("qrtr: Invalid version %d\n", ver); + return -EINVAL; + } + + return ALIGN(size, 4) + hdrlen; +} +EXPORT_SYMBOL(qrtr_peek_pkt_size); + /** * qrtr_endpoint_post() - post incoming data * @ep: endpoint handle diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h index 3f2d28696062..2f7bd02b21ae 100644 --- a/net/qrtr/qrtr.h +++ b/net/qrtr/qrtr.h @@ -33,4 +33,5 @@ int qrtr_ns_init(void); void qrtr_ns_remove(void); +int qrtr_peek_pkt_size(const void *data); #endif From 3f161d1572e7a4dee709507f71433c1a4d0b9c64 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:15:10 -0700 Subject: [PATCH 4/9] net: qrtr: mhi: synchronize qrtr and mhi preparation A dl callback can be received anytime after mhi_prepare_for_transfer has been called. There is a window where the callback may happen before the probe initializes the qrtr_mhi_dev state. Move mhi_prepare_for_transfer after the registering the endpoint. Once moved, the reverse can happen where qrtr will try to send a packet before the channels are prepared. Add a wait in the sending path to ensure the channels are prepared before trying to do a ul transfer. Change-Id: Ib5bf34ec8c1fa56a8ce29c89449d062de7133846 Signed-off-by: Bhaumik Bhatt Signed-off-by: Chris Lew --- net/qrtr/mhi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index 9ced13c0627a..142e675eb496 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -15,6 +16,7 @@ struct qrtr_mhi_dev { struct qrtr_endpoint ep; struct mhi_device *mhi_dev; struct device *dev; + struct completion prepared; }; /* From MHI to QRTR */ @@ -53,6 +55,10 @@ static int qcom_mhi_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) if (skb->sk) sock_hold(skb->sk); + rc = wait_for_completion_interruptible(&qdev->prepared); + if (rc) + goto free_skb; + rc = skb_linearize(skb); if (rc) goto free_skb; @@ -85,6 +91,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, qdev->mhi_dev = mhi_dev; qdev->dev = &mhi_dev->dev; qdev->ep.xmit = qcom_mhi_qrtr_send; + init_completion(&qdev->prepared); dev_set_drvdata(&mhi_dev->dev, qdev); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); @@ -97,6 +104,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, qrtr_endpoint_unregister(&qdev->ep); return rc; } + complete_all(&qdev->prepared); dev_dbg(qdev->dev, "Qualcomm MHI QRTR driver probed\n"); From bf77ef2d638d7b9e1735a13a0f4c07ac3bb34d91 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:32:07 -0700 Subject: [PATCH 5/9] net: qrtr: Return success if control port is not bound If a port tries to send a message before the name service binds to the control port, then that message should succeed. This will allow clients to operate normally until the name service comes online and causes a net reset to restart all the ports. Change-Id: Ica0a5e45df0e51f282e5bf426d11ff8e41b339c4 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 27e3785c8596..90c6c188eea7 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -980,6 +980,10 @@ static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb, struct sock *sk = skb->sk; ipc = qrtr_port_lookup(to->sq_port); + if (!ipc && to->sq_port == QRTR_PORT_CTRL) { + kfree_skb(skb); + return 0; + } if (!ipc || &ipc->sk == skb->sk) { /* do not send to self */ if (ipc) qrtr_port_put(ipc); From 904b1aa3532458ac6a3d224eb29a7d0c5eea7870 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:32:18 -0700 Subject: [PATCH 6/9] net: qrtr: Add permission rules for QTI targets Give control port access to processes that have root user priveleges, AID_VENDOR_QRTR user priveleges, or NET_ADMIN capabilities. Remove the need for SYS_ADMIN priveleges because they will not be granted on any QTI targets. This change squashes the following commits from msm-4.14: commit 17da4da93c96 ("qrtr: Allow net bind service capabilities") commit 56faadfd27b4 ("qrtr: Fix NET_BIND_SERVICE logic") commit 82ea58d60c46 ("net: qrtr: Allow control port access to AID_VENDOR_QRTR") commit 9db4f5803ce1 ("net: qrtr: Allow Root guid process to bind") In addition fix minor format issues. Change-Id: I1441cbf53eb62880333a1031334a5331c1f54a77 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 90c6c188eea7..43f94ea02ee9 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -31,6 +32,8 @@ #define QRTR_STATE_MULTI -2 #define QRTR_STATE_INIT -1 +#define AID_VENDOR_QRTR KGIDT_INIT(2906) + /** * struct qrtr_hdr_v1 - (I|R)PCrouter packet header version 1 * @version: protocol version @@ -864,7 +867,10 @@ static int qrtr_port_assign(struct qrtr_sock *ipc, int *port) rc = xa_alloc_cyclic(&qrtr_ports, port, ipc, QRTR_EPH_PORT_RANGE, &qrtr_ports_next, GFP_KERNEL); - } else if (*port < QRTR_MIN_EPH_SOCKET && !capable(CAP_NET_ADMIN)) { + } else if (*port < QRTR_MIN_EPH_SOCKET && + !(capable(CAP_NET_ADMIN) || + in_egroup_p(AID_VENDOR_QRTR) || + in_egroup_p(GLOBAL_ROOT_GID))) { rc = -EACCES; } else if (*port == QRTR_PORT_CTRL) { rc = xa_insert(&qrtr_ports, 0, ipc, GFP_KERNEL); From ed4fba13e929b27cbaf988c177d206e99d23170e Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:33:45 -0700 Subject: [PATCH 7/9] net: qrtr: Remove rcu logic for port lookup/remove synchronize_rcu() is slow and can create delays on creating/removing sockets. Some clients have latency requirements for spinning off a thread that creates a client and lookup sockets for some sessions. They close the lookup socket during this sequence which causes delays. Each call to synchronize_rcu takes 20-40 ms which is too long. Remove the rcu logic and change qrtr_port_lock to a spinlock so it can be used in qrtr_port_lookup which may be used in atomic context. Change-Id: I3244e31d3ca0599df794d3936033735bcdd3e972 Signed-off-by: Jay Jayanna Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 43f94ea02ee9..8b0201b62f41 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -123,6 +123,7 @@ static DECLARE_RWSEM(qrtr_epts_lock); /* local port allocation management */ static DEFINE_XARRAY_ALLOC(qrtr_ports); u32 qrtr_ports_next = QRTR_MIN_EPH_SOCKET; +static DEFINE_SPINLOCK(qrtr_port_lock); /** * struct qrtr_node - endpoint node @@ -764,15 +765,16 @@ EXPORT_SYMBOL_GPL(qrtr_endpoint_unregister); static struct qrtr_sock *qrtr_port_lookup(int port) { struct qrtr_sock *ipc; + unsigned long flags; if (port == QRTR_PORT_CTRL) port = 0; - rcu_read_lock(); + spin_lock_irqsave(&qrtr_port_lock, flags); ipc = xa_load(&qrtr_ports, port); if (ipc) sock_hold(&ipc->sk); - rcu_read_unlock(); + spin_unlock_irqrestore(&qrtr_port_lock, flags); return ipc; } @@ -834,6 +836,7 @@ static void qrtr_send_del_client(struct qrtr_sock *ipc) static void qrtr_port_remove(struct qrtr_sock *ipc) { int port = ipc->us.sq_port; + unsigned long flags; qrtr_send_del_client(ipc); @@ -842,11 +845,9 @@ static void qrtr_port_remove(struct qrtr_sock *ipc) __sock_put(&ipc->sk); + spin_lock_irqsave(&qrtr_port_lock, flags); xa_erase(&qrtr_ports, port); - - /* Ensure that if qrtr_port_lookup() did enter the RCU read section we - * wait for it to up increment the refcount */ - synchronize_rcu(); + spin_unlock_irqrestore(&qrtr_port_lock, flags); } /* Assign port number to socket. @@ -913,6 +914,7 @@ static int __qrtr_bind(struct socket *sock, { struct qrtr_sock *ipc = qrtr_sk(sock->sk); struct sock *sk = sock->sk; + unsigned long flags; int port; int rc; @@ -920,10 +922,17 @@ static int __qrtr_bind(struct socket *sock, if (!zapped && addr->sq_port == ipc->us.sq_port) return 0; + spin_lock_irqsave(&qrtr_port_lock, flags); port = addr->sq_port; rc = qrtr_port_assign(ipc, &port); - if (rc) + if (rc) { + spin_unlock_irqrestore(&qrtr_port_lock, flags); return rc; + } + + if (port == QRTR_PORT_CTRL) + qrtr_reset_ports(); + spin_unlock_irqrestore(&qrtr_port_lock, flags); /* unbind previous, if any */ if (!zapped) @@ -932,10 +941,6 @@ static int __qrtr_bind(struct socket *sock, sock_reset_flag(sk, SOCK_ZAPPED); - /* Notify all open ports about the new controller */ - if (port == QRTR_PORT_CTRL) - qrtr_reset_ports(); - return 0; } From c742a819756a5d69c3551d4020012cb7864edd58 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:33:51 -0700 Subject: [PATCH 8/9] net: qrtr: Move tx_resume handling to work context There will be non-atomic work associated with receiving the tx resume packet from the remote. Move the handling to work function context in preparation for the non-blocking socket blocking improvement. In system heavy load cases the rx work queued in global worker queue is getting delayed and causing clients request timeouts. Create and use separate worker thread to process qrtr rx packets. Change-Id: I2389bdcffba068258650bfbb579f8cb98a0ac928 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 8b0201b62f41..9d47c1b6b017 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -4,6 +4,7 @@ * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include #include #include #include @@ -136,6 +137,9 @@ static DEFINE_SPINLOCK(qrtr_port_lock); * @hello_sent: hello packet sent to endpoint * @rx_queue: receive queue * @item: list item for broadcast list + * @kworker: worker thread for recv work + * @task: task to run the worker thread + * @read_data: scheduled work for recv work */ struct qrtr_node { struct mutex ep_lock; @@ -149,6 +153,10 @@ struct qrtr_node { struct sk_buff_head rx_queue; struct list_head item; + + struct kthread_worker kworker; + struct task_struct *task; + struct kthread_work read_data; }; /** @@ -227,6 +235,8 @@ static void __qrtr_node_release(struct kref *kref) list_del(&node->item); up_write(&qrtr_epts_lock); + kthread_flush_worker(&node->kworker); + kthread_stop(node->task); skb_queue_purge(&node->rx_queue); /* Free tx flow counters */ @@ -235,6 +245,7 @@ static void __qrtr_node_release(struct kref *kref) radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot); kfree(flow); } + kfree(node); } @@ -620,7 +631,8 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) } if (cb->type == QRTR_TYPE_RESUME_TX) { - qrtr_tx_resume(node, skb); + skb_queue_tail(&node->rx_queue, skb); + kthread_queue_work(&node->kworker, &node->read_data); } else { ipc = qrtr_port_lookup(cb->dst_port); if (!ipc) { @@ -671,6 +683,21 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt, return skb; } +/* Handle not atomic operations for a received packet. */ +static void qrtr_node_rx_work(struct kthread_work *work) +{ + struct qrtr_node *node = container_of(work, struct qrtr_node, + read_data); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&node->rx_queue)) != NULL) { + struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb; + + if (cb->type == QRTR_TYPE_RESUME_TX) + qrtr_tx_resume(node, skb); + } +} + /** * qrtr_endpoint_register() - register a new endpoint * @ep: endpoint to register @@ -697,6 +724,14 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) node->ep = ep; atomic_set(&node->hello_sent, 0); + kthread_init_work(&node->read_data, qrtr_node_rx_work); + kthread_init_worker(&node->kworker); + node->task = kthread_run(kthread_worker_fn, &node->kworker, "qrtr_rx"); + if (IS_ERR(node->task)) { + kfree(node); + return -ENOMEM; + } + INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); mutex_init(&node->qrtr_tx_lock); From 188c64d2603e5d320afc063f266be4c1903568c3 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 11 Oct 2022 13:34:35 -0700 Subject: [PATCH 9/9] net: qrtr: Add forwarding support based on net id Add support for QRTR to forward messages between network clusters. The network subnet id's are attached to the QRTR subnodes in the MHI and RPMSG/GLINK nodes. The current forwarding decisions are done based on the net ids and are wrapped in a single function for easier maintenance of forwarding decisions later on. The function will return true once it determines a subnet has no connection to another subnet. The NEW_SERVER, DEL_SERVER, and DEL_CLIENT control messages should be forwarded while the DATA and RESUME_TX commands should be passed along to their destination node. The nameservice is expected to send NEW_SERVER commands for it's entire database instead of just the local service database with these changes. This is to make sure new nodes get service notifications that came earlier and were meant to be forwarded to the new node. This change squashes the following commits from msm-4.14: commit e5f6a6769ceb ("net: qrtr: Add forwarding support based on net id") commit 4516ab18718d ("net: qrtr: avoid deadlock in case of recursive lock for node list") commit dc2d47cd12d3 ("net: qrtr: Only update pkts with broadcast node ID") commit 4277aebeff91 ("net: qrtr: Free skb if qrtr_node_lookup fails during qrtr_fwd_pkt") In addition fix minor format issues and update copyright. Change-Id: Iaf01dd212b07dfbb13ec087ea0aae980cda7c2fd Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 140 ++++++++++++++++++++++++++++++++++++++++++--- net/qrtr/mhi.c | 36 +++++++++++- net/qrtr/qrtr.h | 3 +- net/qrtr/smd.c | 9 ++- net/qrtr/tun.c | 2 +- 5 files changed, 177 insertions(+), 13 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 9d47c1b6b017..c6455ce5364f 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(qrtr_port_lock); * @ep: endpoint * @ref: reference count for node * @nid: node id + * @net_id: network cluster identifer * @qrtr_tx_flow: tree of qrtr_tx_flow, keyed by node << 32 | port * @qrtr_tx_lock: lock for qrtr_tx_flow inserts * @hello_sent: hello packet sent to endpoint @@ -146,6 +147,7 @@ struct qrtr_node { struct qrtr_endpoint *ep; struct kref ref; unsigned int nid; + unsigned int net_id; atomic_t hello_sent; struct radix_tree_root qrtr_tx_flow; @@ -405,10 +407,17 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, return 0; } - confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type); - if (confirm_rx < 0) { - kfree_skb(skb); - return confirm_rx; + /* If sk is null, this is a forwarded packet and should not wait */ + if (!skb->sk) { + struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb; + + confirm_rx = cb->confirm_rx; + } else { + confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type); + if (confirm_rx < 0) { + kfree_skb(skb); + return confirm_rx; + } } hdr = skb_push(skb, sizeof(*hdr)); @@ -630,7 +639,10 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) qrtr_node_assign(node, le32_to_cpu(pkt->server.node)); } - if (cb->type == QRTR_TYPE_RESUME_TX) { + /* All control packets and non-local destined data packets should be + * queued to the worker for forwarding handling. + */ + if (cb->type != QRTR_TYPE_DATA || cb->dst_node != qrtr_local_nid) { skb_queue_tail(&node->rx_queue, skb); kthread_queue_work(&node->kworker, &node->read_data); } else { @@ -683,6 +695,77 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt, return skb; } +static bool qrtr_must_forward(struct qrtr_node *src, + struct qrtr_node *dst, u32 type) +{ + /* Node structure is not maintained for local processor. + * Hence src is null in that case. + */ + if (!src) + return true; + + if (!dst) + return false; + + if (type == QRTR_TYPE_HELLO || type == QRTR_TYPE_RESUME_TX) + return false; + + if (dst == src || dst->nid == QRTR_EP_NID_AUTO) + return false; + + if (abs(dst->net_id - src->net_id) > 1) + return true; + + return false; +} + +static void qrtr_fwd_ctrl_pkt(struct qrtr_node *src, struct sk_buff *skb) +{ + struct qrtr_node *node; + struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb; + + down_read(&qrtr_epts_lock); + list_for_each_entry(node, &qrtr_all_epts, item) { + struct sockaddr_qrtr from; + struct sockaddr_qrtr to; + struct sk_buff *skbn; + + if (!qrtr_must_forward(src, node, cb->type)) + continue; + + skbn = skb_clone(skb, GFP_KERNEL); + if (!skbn) + break; + + from.sq_family = AF_QIPCRTR; + from.sq_node = cb->src_node; + from.sq_port = cb->src_port; + + to.sq_family = AF_QIPCRTR; + to.sq_node = node->nid; + to.sq_port = QRTR_PORT_CTRL; + + qrtr_node_enqueue(node, skbn, cb->type, &from, &to); + } + up_read(&qrtr_epts_lock); +} + +static void qrtr_fwd_pkt(struct sk_buff *skb, struct qrtr_cb *cb) +{ + 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 *node; + + node = qrtr_node_lookup(cb->dst_node); + if (!node) { + kfree_skb(skb); + return; + } + + qrtr_node_enqueue(node, skb, cb->type, &from, &to); + qrtr_node_release(node); +} + /* Handle not atomic operations for a received packet. */ static void qrtr_node_rx_work(struct kthread_work *work) { @@ -692,9 +775,31 @@ static void qrtr_node_rx_work(struct kthread_work *work) while ((skb = skb_dequeue(&node->rx_queue)) != NULL) { struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb; + struct qrtr_sock *ipc; - if (cb->type == QRTR_TYPE_RESUME_TX) + if (cb->type != QRTR_TYPE_DATA) + qrtr_fwd_ctrl_pkt(node, skb); + + if (cb->type == QRTR_TYPE_RESUME_TX) { + if (cb->dst_node != qrtr_local_nid) { + qrtr_fwd_pkt(skb, cb); + continue; + } qrtr_tx_resume(node, skb); + } else if (cb->dst_node != qrtr_local_nid && + cb->type == QRTR_TYPE_DATA) { + qrtr_fwd_pkt(skb, cb); + } else { + ipc = qrtr_port_lookup(cb->dst_port); + if (!ipc) { + kfree_skb(skb); + } else { + if (sock_queue_rcv_skb(&ipc->sk, skb)) + kfree_skb(skb); + + qrtr_port_put(ipc); + } + } } } @@ -706,7 +811,7 @@ static void qrtr_node_rx_work(struct kthread_work *work) * * The specified endpoint must have the xmit function pointer set on call. */ -int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) +int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id) { struct qrtr_node *node; @@ -735,7 +840,8 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); mutex_init(&node->qrtr_tx_lock); - qrtr_node_assign(node, nid); + qrtr_node_assign(node, node->nid); + node->net_id = net_id; down_write(&qrtr_epts_lock); list_add(&node->item, &qrtr_all_epts); @@ -1094,7 +1200,9 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) __le32 qrtr_type = cpu_to_le32(QRTR_TYPE_DATA); struct qrtr_sock *ipc = qrtr_sk(sock->sk); struct sock *sk = sock->sk; + struct qrtr_ctrl_pkt pkt; struct qrtr_node *node; + struct qrtr_node *srv_node; struct sk_buff *skb; size_t plen; u32 type; @@ -1132,6 +1240,7 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) } node = NULL; + srv_node = NULL; if (addr->sq_node == QRTR_NODE_BCAST) { if (addr->sq_port != QRTR_PORT_CTRL && qrtr_local_nid != QRTR_NODE_BCAST) { @@ -1184,10 +1293,23 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) } type = le32_to_cpu(qrtr_type); - if (addr->sq_port == QRTR_PORT_CTRL && type == QRTR_TYPE_NEW_SERVER) + if (addr->sq_port == QRTR_PORT_CTRL && type == QRTR_TYPE_NEW_SERVER) { ipc->state = QRTR_STATE_MULTI; + /* drop new server cmds that are not forwardable to dst node*/ + skb_copy_bits(skb, 0, &pkt, sizeof(pkt)); + srv_node = qrtr_node_lookup(pkt.server.node); + if (!qrtr_must_forward(srv_node, node, type)) { + rc = 0; + kfree_skb(skb); + qrtr_node_release(srv_node); + goto out_node; + } + qrtr_node_release(srv_node); + } + rc = enqueue_fn(node, skb, type, &ipc->us, addr); + if (rc >= 0) rc = len; diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index 142e675eb496..e722967fea77 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -7,7 +7,10 @@ #include #include #include +#include +#include #include +#include #include #include "qrtr.h" @@ -78,10 +81,38 @@ static int qcom_mhi_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) return rc; } +static void qrtr_mhi_of_parse(struct mhi_device *mhi_dev, + u32 *net_id) +{ + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + struct device_node *np = NULL; + struct pci_dev *pci_device; + u32 dev_id, nid; + int rc; + + *net_id = QRTR_EP_NET_ID_AUTO; + + np = of_find_compatible_node(np, NULL, "qcom,qrtr-mhi"); + if (!np) + return; + + rc = of_property_read_u32(np, "qcom,dev-id", &dev_id); + if (!rc) { + pci_device = to_pci_dev(mhi_cntrl->cntrl_dev); + if (pci_device->device == dev_id) { + rc = of_property_read_u32(np, "qcom,net-id", &nid); + if (!rc) + *net_id = nid; + } + } + of_node_put(np); +} + static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) { struct qrtr_mhi_dev *qdev; + u32 net_id; int rc; qdev = devm_kzalloc(&mhi_dev->dev, sizeof(*qdev), GFP_KERNEL); @@ -94,7 +125,10 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, init_completion(&qdev->prepared); dev_set_drvdata(&mhi_dev->dev, qdev); - rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); + + qrtr_mhi_of_parse(mhi_dev, &net_id); + + rc = qrtr_endpoint_register(&qdev->ep, net_id); if (rc) return rc; diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h index 2f7bd02b21ae..322a46498e40 100644 --- a/net/qrtr/qrtr.h +++ b/net/qrtr/qrtr.h @@ -8,6 +8,7 @@ struct sk_buff; /* endpoint node id auto assignment */ #define QRTR_EP_NID_AUTO (-1) +#define QRTR_EP_NET_ID_AUTO (1) /** * struct qrtr_endpoint - endpoint handle @@ -23,7 +24,7 @@ struct qrtr_endpoint { struct qrtr_node *node; }; -int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid); +int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id); void qrtr_endpoint_unregister(struct qrtr_endpoint *ep); diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c index 9c7d6085a1dc..46865b66e7ec 100644 --- a/net/qrtr/smd.c +++ b/net/qrtr/smd.c @@ -2,11 +2,13 @@ /* * Copyright (c) 2015, Sony Mobile Communications Inc. * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include +#include #include "qrtr.h" @@ -61,6 +63,7 @@ static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) { struct qrtr_smd_dev *qdev; + u32 net_id; int rc; qdev = devm_kzalloc(&rpdev->dev, sizeof(*qdev), GFP_KERNEL); @@ -71,7 +74,11 @@ static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) qdev->dev = &rpdev->dev; qdev->ep.xmit = qcom_smd_qrtr_send; - rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); + rc = of_property_read_u32(rpdev->dev.of_node, "qcom,net-id", &net_id); + if (rc < 0) + net_id = QRTR_EP_NET_ID_AUTO; + + rc = qrtr_endpoint_register(&qdev->ep, net_id); if (rc) { dev_err(qdev->dev, "endpoint register failed: %d\n", rc); return rc; diff --git a/net/qrtr/tun.c b/net/qrtr/tun.c index 304b41fea5ab..6e8524e10c67 100644 --- a/net/qrtr/tun.c +++ b/net/qrtr/tun.c @@ -44,7 +44,7 @@ static int qrtr_tun_open(struct inode *inode, struct file *filp) filp->private_data = tun; - ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NID_AUTO); + ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NET_ID_AUTO); if (ret) goto out;