From 1da9ab5d385b82522522f7693ab87c866471e7a5 Mon Sep 17 00:00:00 2001 From: Tony Truong Date: Wed, 26 Jan 2022 23:24:05 -0800 Subject: [PATCH 01/13] net: qrtr: Support pm_wakeup_event() based on svc Instead of filtering pm_wakeup_event() call via node ID, add support to do it based on the service ID. This provides more fine control over which incoming packet can wakeup APPS. Change-Id: I1e4a7f6d04a2e37104191bedc5d87070aabc002b Signed-off-by: Tony Truong --- net/qrtr/af_qrtr.c | 24 +++++++++++++++++++++--- net/qrtr/gunyah.c | 5 +++-- net/qrtr/mhi.c | 2 +- net/qrtr/qrtr.h | 14 +++++++++++++- net/qrtr/smd.c | 16 +++++++++++++++- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index e1d6c1813173..7bfad4db18ad 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -185,6 +185,8 @@ struct qrtr_node { struct wakeup_source *ws; void *ilc; + + struct xarray no_wake_svc; /* services that will not wake up APPS */ }; /** @@ -799,6 +801,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) unsigned int ver; size_t hdrlen; int errcode; + int svc_id; if (len == 0 || len & 3) return -EINVAL; @@ -906,8 +909,9 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) goto err; } - /* Force wakeup for all packets except for sensors */ - if (node->nid != 9) + /* Force wakeup based on services */ + svc_id = qrtr_get_service_id(cb->src_node, cb->src_port); + if (!xa_load(&node->no_wake_svc, svc_id)) pm_wakeup_ws_event(node->ws, qrtr_wakeup_ms, true); qrtr_port_put(ipc); @@ -1115,13 +1119,16 @@ static void qrtr_hello_work(struct kthread_work *work) * @ep: endpoint to register * @nid: desired node id; may be QRTR_EP_NID_AUTO for auto-assignment * @rt: flag to notify real time low latency endpoint + * @no_wake: array of services to not wake up * Return: 0 on success; negative error code on failure * * The specified endpoint must have the xmit function pointer set on call. */ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id, - bool rt) + bool rt, struct qrtr_array *no_wake) { + int rc, i; + size_t size; struct qrtr_node *node; struct sched_param param = {.sched_priority = 1}; @@ -1151,6 +1158,17 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id, if (rt) sched_setscheduler(node->task, SCHED_FIFO, ¶m); + xa_init(&node->no_wake_svc); + size = no_wake ? no_wake->size : 0; + for (i = 0; i < size; i++) { + rc = xa_insert(&node->no_wake_svc, no_wake->arr[i], node, + GFP_KERNEL); + if (rc) { + kfree(node); + return rc; + } + } + INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); mutex_init(&node->qrtr_tx_lock); diff --git a/net/qrtr/gunyah.c b/net/qrtr/gunyah.c index 38297f8d375b..f96d9dcc163d 100644 --- a/net/qrtr/gunyah.c +++ b/net/qrtr/gunyah.c @@ -454,7 +454,8 @@ static int qrtr_gunyah_rm_cb(struct notifier_block *nb, unsigned long cmd, if (vm_status_payload->vm_status == GH_RM_VM_STATUS_READY) { qrtr_gunyah_fifo_init(qdev); - if (qrtr_endpoint_register(&qdev->ep, QRTR_EP_NET_ID_AUTO, false)) { + if (qrtr_endpoint_register(&qdev->ep, QRTR_EP_NET_ID_AUTO, + false, NULL)) { pr_err("%s: endpoint register failed\n", __func__); return NOTIFY_DONE; } @@ -649,7 +650,7 @@ static int qrtr_gunyah_probe(struct platform_device *pdev) qdev->ep.xmit = qrtr_gunyah_send; if (!qdev->master) { ret = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NET_ID_AUTO, - false); + false, NULL); if (ret) goto register_fail; } diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index d21a53449559..30e8c8203d47 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -130,7 +130,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, qrtr_mhi_of_parse(mhi_dev, &net_id, &rt); - rc = qrtr_endpoint_register(&qdev->ep, net_id, rt); + rc = qrtr_endpoint_register(&qdev->ep, net_id, rt, NULL); if (rc) return rc; diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h index f0ee970a8546..95d9d720e0ef 100644 --- a/net/qrtr/qrtr.h +++ b/net/qrtr/qrtr.h @@ -26,8 +26,20 @@ struct qrtr_endpoint { struct qrtr_node *node; }; +/** + * struct qrtr_array - array with size + * @arr: elements in the array + * @size: number of elements + * + * An array with its size provided. + */ +struct qrtr_array { + u32 *arr; + size_t size; +}; + int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id, - bool rt); + bool rt, struct qrtr_array *no_wake); void qrtr_endpoint_unregister(struct qrtr_endpoint *ep); diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c index cc2381c08f26..228b4407a7b1 100644 --- a/net/qrtr/smd.c +++ b/net/qrtr/smd.c @@ -66,7 +66,9 @@ 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_array svc_arr = {NULL, 0}; struct qrtr_smd_dev *qdev; + size_t size; u32 net_id; bool rt; int rc; @@ -85,7 +87,19 @@ static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) rt = of_property_read_bool(rpdev->dev.of_node, "qcom,low-latency"); - rc = qrtr_endpoint_register(&qdev->ep, net_id, rt); + size = of_property_count_u32_elems(rpdev->dev.of_node, "qcom,no-wake-svc"); + if (size > 0) { + svc_arr.size = size; + svc_arr.arr = kmalloc_array(size, sizeof(u32), GFP_KERNEL); + if (!svc_arr.arr) + return -ENOMEM; + + of_property_read_u32_array(rpdev->dev.of_node, "qcom,no-wake-svc", + svc_arr.arr, size); + } + + rc = qrtr_endpoint_register(&qdev->ep, net_id, rt, &svc_arr); + kfree(svc_arr.arr); if (rc) { dev_err(qdev->dev, "endpoint register failed: %d, low-latency: %d\n", rc, rt); return rc; From ab95e090ae9e244add7342e7072be8f34f4e5ccc Mon Sep 17 00:00:00 2001 From: Tony Truong Date: Fri, 25 Feb 2022 01:21:16 -0800 Subject: [PATCH 02/13] net: qrtr: fix type for size in smd The return value for of_property_count_u32_elems can be negative in the case of an error or the property is not found. It is incorrect to use size_t, unsigned type, as this can cause an overflow. Switch size from type size_t to int. Change-Id: Ica0425abd034b82994ab32087f04d602ce3dd9e9 Signed-off-by: Tony Truong --- net/qrtr/smd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c index 228b4407a7b1..cc7bd0d61abb 100644 --- a/net/qrtr/smd.c +++ b/net/qrtr/smd.c @@ -68,8 +68,8 @@ static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) { struct qrtr_array svc_arr = {NULL, 0}; struct qrtr_smd_dev *qdev; - size_t size; u32 net_id; + int size; bool rt; int rc; From 69dbe4ac191d1f6eb7d8234e964acf214f338967 Mon Sep 17 00:00:00 2001 From: Tony Truong Date: Wed, 16 Mar 2022 16:11:12 -0700 Subject: [PATCH 03/13] net: qrtr: get svc_id before queueing sk_buff In qrtr_endpoint_post, getting svc_id based on sk_buff->cb after sk_buff has been queued to the endpoint leads to a potential use-after-free scenario. To avoid this race condition, get the service ID via sk_buff->cb before queueing the sk_buff. Change-Id: I53205fdffc08fd6dc48fd158c7fe5966f38aa978 Signed-off-by: Tony Truong --- net/qrtr/af_qrtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 7bfad4db18ad..db0be1473616 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -893,6 +893,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) /* All control packets and non-local destined data packets should be * queued to the worker for forwarding handling. */ + svc_id = qrtr_get_service_id(cb->src_node, cb->src_port); 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); @@ -910,7 +911,6 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) } /* Force wakeup based on services */ - svc_id = qrtr_get_service_id(cb->src_node, cb->src_port); if (!xa_load(&node->no_wake_svc, svc_id)) pm_wakeup_ws_event(node->ws, qrtr_wakeup_ms, true); From 0909e0e1ee774c5ae68a41e766b885364d9fb34e Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Mon, 28 Mar 2022 17:30:03 -0700 Subject: [PATCH 04/13] net: qrtr: Add non blocking option for tx_resume QRTR should support asynchronous sends even with the tx flow control enabled. Add a timeout to the tx_wait and send a zero size packet to nonblocking sockets after the remote port is no longer flow controlled. This zero size packet will act as a notification for clients to start sending again. Change-Id: I5040b98fe666a1a9c27994dc66b321328c2e4c66 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 162 ++++++++++++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 45 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index db0be1473616..733042b96491 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -189,16 +189,26 @@ struct qrtr_node { struct xarray no_wake_svc; /* services that will not wake up APPS */ }; +struct qrtr_tx_flow_waiter { + struct list_head node; + struct sock *sk; +}; + /** * struct qrtr_tx_flow - tx flow control * @resume_tx: waiters for a resume tx from the remote * @pending: number of waiting senders * @tx_failed: indicates that a message with confirm_rx flag was lost + * @waiters: list of ports to notify when this flow resumes + * @lock: lock to protect flow variables */ struct qrtr_tx_flow { struct wait_queue_head resume_tx; int pending; int tx_failed; + struct list_head waiters; + /* protect above flow variables */ + spinlock_t lock; }; #define QRTR_TX_FLOW_HIGH 10 @@ -206,10 +216,10 @@ struct qrtr_tx_flow { static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, - struct sockaddr_qrtr *to); + struct sockaddr_qrtr *to, unsigned int flags); static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, - struct sockaddr_qrtr *to); + struct sockaddr_qrtr *to, unsigned int flags); static struct qrtr_sock *qrtr_port_lookup(int port); static void qrtr_port_put(struct qrtr_sock *ipc); @@ -395,6 +405,8 @@ static inline int kref_put_rwsem_lock(struct kref *kref, static void __qrtr_node_release(struct kref *kref) { struct qrtr_node *node = container_of(kref, struct qrtr_node, ref); + struct qrtr_tx_flow_waiter *waiter; + struct qrtr_tx_flow_waiter *temp; struct radix_tree_iter iter; struct qrtr_tx_flow *flow; unsigned long flags; @@ -419,11 +431,18 @@ static void __qrtr_node_release(struct kref *kref) wakeup_source_unregister(node->ws); /* Free tx flow counters */ + mutex_lock(&node->qrtr_tx_lock); radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { flow = *slot; + list_for_each_entry_safe(waiter, temp, &flow->waiters, node) { + list_del(&waiter->node); + sock_put(waiter->sk); + kfree(waiter); + } radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot); kfree(flow); } + mutex_unlock(&node->qrtr_tx_lock); kfree(node); } @@ -451,26 +470,48 @@ static void qrtr_node_release(struct qrtr_node *node) */ static void qrtr_tx_resume(struct qrtr_node *node, struct sk_buff *skb) { + struct qrtr_tx_flow_waiter *waiter; + struct qrtr_tx_flow_waiter *temp; struct qrtr_ctrl_pkt pkt = {0,}; struct qrtr_tx_flow *flow; + struct sockaddr_qrtr src; + struct qrtr_sock *ipc; + struct sk_buff *skbn; + unsigned long flags; unsigned long key; - u64 remote_node; - u32 remote_port; skb_copy_bits(skb, 0, &pkt, sizeof(pkt)); - remote_node = le32_to_cpu(pkt.client.node); - remote_port = le32_to_cpu(pkt.client.port); - key = remote_node << 32 | remote_port; + if (le32_to_cpu(pkt.cmd) != QRTR_TYPE_RESUME_TX) + return; - rcu_read_lock(); + src.sq_family = AF_QIPCRTR; + src.sq_node = le32_to_cpu(pkt.client.node); + src.sq_port = le32_to_cpu(pkt.client.port); + key = (u64)src.sq_node << 32 | src.sq_port; + + mutex_lock(&node->qrtr_tx_lock); flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); - if (flow) { - spin_lock(&flow->resume_tx.lock); - flow->pending = 0; - spin_unlock(&flow->resume_tx.lock); - wake_up_interruptible_all(&flow->resume_tx); + mutex_unlock(&node->qrtr_tx_lock); + if (!flow) + return; + + spin_lock_irqsave(&flow->lock, flags); + flow->pending = 0; + wake_up_interruptible_all(&flow->resume_tx); + + list_for_each_entry_safe(waiter, temp, &flow->waiters, node) { + list_del(&waiter->node); + + skbn = alloc_skb(0, GFP_ATOMIC); + if (skbn) { + ipc = qrtr_sk(waiter->sk); + qrtr_local_enqueue(NULL, skbn, QRTR_TYPE_RESUME_TX, + &src, &ipc->us, 0); + } + sock_put(waiter->sk); + kfree(waiter); } + spin_unlock_irqrestore(&flow->lock, flags); consume_skb(skb); } @@ -490,24 +531,31 @@ static void qrtr_tx_resume(struct qrtr_node *node, struct sk_buff *skb) * * Return: 1 if confirm_rx should be set, 0 otherwise or errno failure */ -static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port, - int type) +static int qrtr_tx_wait(struct qrtr_node *node, struct sockaddr_qrtr *to, + struct sock *sk, int type, unsigned int flags) { - unsigned long key = (u64)dest_node << 32 | dest_port; + unsigned long key = (u64)to->sq_node << 32 | to->sq_port; + struct qrtr_tx_flow_waiter *waiter; struct qrtr_tx_flow *flow; int confirm_rx = 0; - int ret; + long timeo; + long ret; /* Never set confirm_rx on non-data packets */ if (type != QRTR_TYPE_DATA) return 0; + /* Assume sk is set correctly for all data type packets */ + timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); + mutex_lock(&node->qrtr_tx_lock); flow = radix_tree_lookup(&node->qrtr_tx_flow, key); if (!flow) { flow = kzalloc(sizeof(*flow), GFP_KERNEL); if (flow) { + INIT_LIST_HEAD(&flow->waiters); init_waitqueue_head(&flow->resume_tx); + spin_lock_init(&flow->lock); if (radix_tree_insert(&node->qrtr_tx_flow, key, flow)) { kfree(flow); flow = NULL; @@ -520,11 +568,13 @@ static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port, if (!flow) return 1; - spin_lock_irq(&flow->resume_tx.lock); - ret = wait_event_interruptible_locked_irq(flow->resume_tx, - flow->pending < QRTR_TX_FLOW_HIGH || - flow->tx_failed || - !node->ep); + spin_lock_irq(&flow->lock); + ret = wait_event_interruptible_lock_irq_timeout(flow->resume_tx, + flow->pending < QRTR_TX_FLOW_HIGH || + flow->tx_failed || + !node->ep, + flow->lock, + timeo); if (ret < 0) { confirm_rx = ret; } else if (!node->ep) { @@ -532,11 +582,32 @@ static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port, } else if (flow->tx_failed) { flow->tx_failed = 0; confirm_rx = 1; + } else if (!ret && flow->pending >= QRTR_TX_FLOW_HIGH) { + list_for_each_entry(waiter, &flow->waiters, node) { + if (waiter->sk == sk) { + spin_unlock_irq(&flow->lock); + return -EAGAIN; + } + } + + waiter = kzalloc(sizeof(*waiter), GFP_ATOMIC); + if (!waiter) { + spin_unlock_irq(&flow->lock); + return -ENOMEM; + } + waiter->sk = sk; + sock_hold(sk); + list_add_tail(&waiter->node, &flow->waiters); + + confirm_rx = -EAGAIN; + QRTR_INFO(node->ilc, "new waiter %s[%d] for [0x%x:0x%x]\n", + current->comm, current->pid, + to->sq_node, to->sq_port); } else { flow->pending++; confirm_rx = flow->pending == QRTR_TX_FLOW_LOW; } - spin_unlock_irq(&flow->resume_tx.lock); + spin_unlock_irq(&flow->lock); return confirm_rx; } @@ -560,20 +631,20 @@ static void qrtr_tx_flow_failed(struct qrtr_node *node, int dest_node, unsigned long key = (u64)dest_node << 32 | dest_port; struct qrtr_tx_flow *flow; - rcu_read_lock(); + mutex_lock(&node->qrtr_tx_lock); flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); + mutex_unlock(&node->qrtr_tx_lock); if (flow) { - spin_lock_irq(&flow->resume_tx.lock); + spin_lock_irq(&flow->lock); flow->tx_failed = 1; - spin_unlock_irq(&flow->resume_tx.lock); + spin_unlock_irq(&flow->lock); } } /* Pass an outgoing packet socket buffer to the endpoint driver. */ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, - struct sockaddr_qrtr *to) + struct sockaddr_qrtr *to, unsigned int flags) { struct qrtr_hdr_v1 *hdr; size_t len = skb->len; @@ -594,7 +665,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, confirm_rx = cb->confirm_rx; } else { - confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type); + confirm_rx = qrtr_tx_wait(node, to, skb->sk, type, flags); if (confirm_rx < 0) { kfree_skb(skb); return confirm_rx; @@ -1002,7 +1073,7 @@ static void qrtr_fwd_ctrl_pkt(struct qrtr_node *src, struct sk_buff *skb) to.sq_node = node->nid; to.sq_port = QRTR_PORT_CTRL; - qrtr_node_enqueue(node, skbn, cb->type, &from, &to); + qrtr_node_enqueue(node, skbn, cb->type, &from, &to, 0); } up_read(&qrtr_epts_lock); } @@ -1019,7 +1090,7 @@ static void qrtr_fwd_pkt(struct sk_buff *skb, struct qrtr_cb *cb) return; } - qrtr_node_enqueue(node, skb, cb->type, &from, &to); + qrtr_node_enqueue(node, skb, cb->type, &from, &to, 0); qrtr_node_release(node); } @@ -1110,7 +1181,7 @@ static void qrtr_hello_work(struct kthread_work *work) pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO); from.sq_node = qrtr_local_nid; to.sq_node = node->nid; - qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to); + qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to, 0); qrtr_port_put(ctrl); } @@ -1237,7 +1308,7 @@ static void qrtr_fwd_del_proc(struct qrtr_node *src, unsigned int nid) from.sq_node = src->nid; to.sq_node = dst->nid; - qrtr_node_enqueue(dst, skb, QRTR_TYPE_DEL_PROC, &from, &to); + qrtr_node_enqueue(dst, skb, QRTR_TYPE_DEL_PROC, &from, &to, 0); } } @@ -1270,7 +1341,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) skb = qrtr_alloc_ctrl_packet(&pkt, GFP_ATOMIC); if (skb) { pkt->cmd = cpu_to_le32(QRTR_TYPE_BYE); - qrtr_local_enqueue(NULL, skb, QRTR_TYPE_BYE, &src, &dst); + qrtr_local_enqueue(NULL, skb, QRTR_TYPE_BYE, &src, &dst, 0); } spin_unlock_irqrestore(&qrtr_nodes_lock, flags); @@ -1343,7 +1414,7 @@ static void qrtr_send_del_client(struct qrtr_sock *ipc) skb_set_owner_w(skb, &ipc->sk); if (ipc->state == QRTR_STATE_MULTI) { - qrtr_bcast_enqueue(NULL, skb, type, &ipc->us, &to); + qrtr_bcast_enqueue(NULL, skb, type, &ipc->us, &to, 0); return; } @@ -1359,11 +1430,11 @@ static void qrtr_send_del_client(struct qrtr_sock *ipc) } skb_set_owner_w(skbn, &ipc->sk); - qrtr_node_enqueue(node, skbn, type, &ipc->us, &to); + qrtr_node_enqueue(node, skbn, type, &ipc->us, &to, 0); qrtr_node_release(node); } exit: - qrtr_local_enqueue(NULL, skb, type, &ipc->us, &to); + qrtr_local_enqueue(NULL, skb, type, &ipc->us, &to, 0); } /* Remove port assignment. */ @@ -1529,7 +1600,7 @@ static int qrtr_bind(struct socket *sock, struct sockaddr *saddr, int len) /* Queue packet to local peer socket. */ static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, - struct sockaddr_qrtr *to) + struct sockaddr_qrtr *to, unsigned int flags) { struct qrtr_sock *ipc; struct qrtr_cb *cb; @@ -1574,7 +1645,7 @@ static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb, /* Queue packet for broadcast. */ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, - struct sockaddr_qrtr *to) + struct sockaddr_qrtr *to, unsigned int flags) { struct sk_buff *skbn; @@ -1587,11 +1658,11 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb, if (!skbn) break; skb_set_owner_w(skbn, skb->sk); - qrtr_node_enqueue(node, skbn, type, from, to); + qrtr_node_enqueue(node, skbn, type, from, to, flags); } up_read(&qrtr_epts_lock); - qrtr_local_enqueue(NULL, skb, type, from, to); + qrtr_local_enqueue(NULL, skb, type, from, to, flags); return 0; } @@ -1600,7 +1671,8 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, msg->msg_name); int (*enqueue_fn)(struct qrtr_node *, struct sk_buff *, int, - struct sockaddr_qrtr *, struct sockaddr_qrtr *); + struct sockaddr_qrtr *, struct sockaddr_qrtr *, + unsigned int); __le32 qrtr_type = cpu_to_le32(QRTR_TYPE_DATA); struct qrtr_sock *ipc = qrtr_sk(sock->sk); struct sock *sk = sock->sk; @@ -1712,7 +1784,7 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) qrtr_node_release(srv_node); } - rc = enqueue_fn(node, skb, type, &ipc->us, addr); + rc = enqueue_fn(node, skb, type, &ipc->us, addr, msg->msg_flags); if (rc >= 0) rc = len; @@ -1747,7 +1819,7 @@ static int qrtr_send_resume_tx(struct qrtr_cb *cb) pkt->client.node = cpu_to_le32(cb->dst_node); pkt->client.port = cpu_to_le32(cb->dst_port); - ret = qrtr_node_enqueue(node, skb, QRTR_TYPE_RESUME_TX, &local, &remote); + ret = qrtr_node_enqueue(node, skb, QRTR_TYPE_RESUME_TX, &local, &remote, 0); qrtr_node_release(node); From 8e0779d50291cb9db0936677d66c7419502e3047 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 30 Mar 2022 13:38:48 -0700 Subject: [PATCH 05/13] net: qrtr: Change port allocation to atomic The xa_alloc_cyclic function cannot be called with GFP_ATOMIC if the context does not have preemption disabled. There was a change to make the port allocation structure synchronize with rcu and therefore was no longer protected with spinlocks. Using RCUs lead to a performance degredation so spinlock synchronization was reinstated. Now that port allocation happens within a spinlock, GFP_KERNEL cannot be used. Change-Id: I2178e190a73a949423d7c7ece1af92ab173e176d Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 733042b96491..dbae3a819bb2 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -1472,7 +1472,7 @@ static int qrtr_port_assign(struct qrtr_sock *ipc, int *port) if (!*port) { rc = xa_alloc_cyclic(&qrtr_ports, port, ipc, QRTR_EPH_PORT_RANGE, &qrtr_ports_next, - GFP_KERNEL); + GFP_ATOMIC); } else if (*port < QRTR_MIN_EPH_SOCKET && !(capable(CAP_NET_ADMIN) || in_egroup_p(AID_VENDOR_QRTR) || From 81f44092bc11a4dc997b9d8abb61ce9f90fee6f6 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 1 Nov 2022 14:39:30 -0700 Subject: [PATCH 06/13] net: qrtr: ns: Fix Announce services for all nodes ns service was notifying only local node ID services after a pci disconnect and reconnect. Fix it to iterate over all nodes and announce services of all nodes. Change-Id: I4f91a71ad847b815318a7de47134f6b9c57dff53 Signed-off-by: Manoharan Vijaya Raghavan Signed-off-by: Chris Lew --- net/qrtr/ns.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index 4f56f64df151..fb248f4fb934 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -228,24 +228,26 @@ static int announce_servers(struct sockaddr_qrtr *sq) struct qrtr_server *srv; struct qrtr_node *node; unsigned long index; + unsigned long node_idx; int ret; - node = node_get(qrtr_ns.local_node); - if (!node) - return 0; - /* Announce the list of servers registered in this node */ - xa_for_each(&node->servers, index, srv) { - ret = service_announce_new(sq, srv); - if (ret < 0) { - if (ret == -ENODEV) - continue; + xa_for_each(&nodes, node_idx, node) { + if (node->id == sq->sq_node) { + pr_info("Avoiding duplicate announce for NODE ID %u\n", node->id); + continue; + } + xa_for_each(&node->servers, index, srv) { + ret = service_announce_new(sq, srv); + if (ret < 0) { + if (ret == -ENODEV) + continue; - pr_err("failed to announce new service %d\n", ret); - return ret; + pr_err("failed to announce new service %d\n", ret); + return ret; + } } } - return 0; } From 948ff2bd1aa4fee2913baeeea764a3ea814cf246 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 13 Apr 2022 17:21:10 -0700 Subject: [PATCH 07/13] net: qrtr: Add ability to override node id from dt Current VM configurations share the defconfig across VMs. This will cause conflict if multiple VMs are running. Add the ability to set an override node id from device tree. Change-Id: I96c4998e3e3976219724dc938775746e181c9f72 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index dbae3a819bb2..60787750d0e2 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -2086,10 +2087,30 @@ static const struct net_proto_family qrtr_family = { .create = qrtr_create, }; +static void qrtr_update_node_id(void) +{ + const char *compat = "qcom,qrtr"; + struct device_node *np = NULL; + u32 node_id; + int ret; + + while ((np = of_find_compatible_node(np, NULL, compat))) { + ret = of_property_read_u32(np, "qcom,node-id", &node_id); + of_node_put(np); + if (ret) + continue; + + qrtr_local_nid = node_id; + break; + } +} + static int __init qrtr_proto_init(void) { int rc; + qrtr_update_node_id(); + rc = proto_register(&qrtr_proto, 1); if (rc) return rc; From 7c2366a03b92ab24a93dd21336b233c412a6ec93 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 13 Apr 2022 17:25:45 -0700 Subject: [PATCH 08/13] net: qrtr: gunyah: Add dynamic fifo allocation If a static carveout is not available for VMs they should be able to fallback to a dynamic allocation from the CMA. Add support to dma alloc contigous memory and share that memory to the VM. Change-Id: Ie8c5b961b530102d9046818a6e0b31a70e05b896 Signed-off-by: Chris Lew --- net/qrtr/gunyah.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/net/qrtr/gunyah.c b/net/qrtr/gunyah.c index f96d9dcc163d..38118890eed6 100644 --- a/net/qrtr/gunyah.c +++ b/net/qrtr/gunyah.c @@ -552,6 +552,24 @@ static struct device_node *qrtr_gunyah_svm_of_parse(struct qrtr_gunyah_dev *qdev return shm_np; } +static int qrtr_gunyah_alloc_fifo(struct qrtr_gunyah_dev *qdev) +{ + struct device *dev = qdev->dev; + resource_size_t size; + + size = FIFO_1_START + FIFO_SIZE; + + qdev->base = dma_alloc_attrs(dev, size, &qdev->res.start, GFP_KERNEL, + DMA_ATTR_FORCE_CONTIGUOUS); + if (!qdev->base) + return -ENOMEM; + + qdev->res.end = qdev->res.start + size - 1; + qdev->size = size; + + return 0; +} + static int qrtr_gunyah_map_memory(struct qrtr_gunyah_dev *qdev) { struct device *dev = qdev->dev; @@ -559,8 +577,11 @@ static int qrtr_gunyah_map_memory(struct qrtr_gunyah_dev *qdev) resource_size_t size; int ret; - np = of_parse_phandle(dev->of_node, "shared-buffer", 0); - if (!np) { + if (qdev->master) { + np = of_parse_phandle(dev->of_node, "shared-buffer", 0); + if (!np) + return qrtr_gunyah_alloc_fifo(qdev); + } else { np = qrtr_gunyah_svm_of_parse(qdev); if (!np) { dev_err(dev, "can't parse shared mem node!\n"); @@ -679,11 +700,32 @@ static int qrtr_gunyah_probe(struct platform_device *pdev) static int qrtr_gunyah_remove(struct platform_device *pdev) { struct qrtr_gunyah_dev *qdev = dev_get_drvdata(&pdev->dev); + struct device_node *np; + gh_vmid_t peer_vmid; + gh_vmid_t self_vmid; cancel_work_sync(&qdev->work); gh_dbl_tx_unregister(qdev->tx_dbl); gh_dbl_rx_unregister(qdev->rx_dbl); + if (!qdev->master) + return 0; + + if (gh_rm_get_vmid(qdev->peer_name, &peer_vmid)) + return 0; + if (gh_rm_get_vmid(GH_PRIMARY_VM, &self_vmid)) + return 0; + qrtr_gunyah_unshare_mem(qdev, self_vmid, peer_vmid); + + np = of_parse_phandle(qdev->dev->of_node, "shared-buffer", 0); + if (np) { + of_node_put(np); + return 0; + } + + dma_free_attrs(qdev->dev, qdev->size, qdev->base, qdev->res.start, + DMA_ATTR_FORCE_CONTIGUOUS); + return 0; } From e1dbd1d03403e1e659611556bb4f15f8258a1e52 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 2 Nov 2022 15:53:29 -0700 Subject: [PATCH 09/13] android: abi_gki_aarch64_qcom: Add skb symbols Add skb_copy_datagram_from_iter used by qrtr in the fragmented skb feature to handle large transmission requests. Change-Id: I4d9ca8295b8df42667723b5b2b8e9b946eeddbe9 Signed-off-by: Chris Lew --- android/abi_gki_aarch64_qcom | 1 + 1 file changed, 1 insertion(+) diff --git a/android/abi_gki_aarch64_qcom b/android/abi_gki_aarch64_qcom index 82228bd61721..45111fa6c5d3 100644 --- a/android/abi_gki_aarch64_qcom +++ b/android/abi_gki_aarch64_qcom @@ -1586,6 +1586,7 @@ sk_alloc skb_clone skb_copy_bits + skb_copy_datagram_from_iter skb_copy_datagram_iter skb_dequeue skb_free_datagram From 164d36655b82d7709f907fb50189255ebd92ad0b Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Fri, 25 Feb 2022 10:39:00 -0800 Subject: [PATCH 10/13] net: qrtr: Add support for tx paged skb alloc Large linear allocations are prone to failure. Add support in qrtr to allocate used paged/fragmented skb's if the data is larger than "SKB_MAX_ALLOC" to improve chances that large packet sends will succeed. This change is mostly useless until transports add support for handling fragmented skb's. Otherwise the large transfers will be linearized in the transport itself. Change-Id: If6411292311db51890f0641464c1565051244378 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 121 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 60787750d0e2..9c04326bf48b 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -642,6 +642,95 @@ static void qrtr_tx_flow_failed(struct qrtr_node *node, int dest_node, } } +static int qrtr_pad_word_pskb(struct sk_buff *skb) +{ + unsigned int padding_len; + unsigned int padto; + int nfrags; + int count; + int i; + + padto = ALIGN(skb->len, 4); + padding_len = padto - skb->len; + if (!padding_len) + return 0; + + count = skb_headlen(skb); + nfrags = skb_shinfo(skb)->nr_frags; + for (i = 0; i < nfrags; i++) { + u32 p_off, p_len, copied; + u32 f_off, f_len; + u32 d_off, d_len; + skb_frag_t *frag; + struct page *p; + u8 *vaddr; + + frag = &skb_shinfo(skb)->frags[i]; + f_off = skb_frag_off(frag); + f_len = skb_frag_size(frag); + if (count + f_len < skb->len) { + count += f_len; + continue; + } + + /* fragment can fit all padding */ + if (count + f_len >= padto) { + skb_frag_foreach_page(frag, f_off, f_len, p, p_off, + p_len, copied) { + if (count + p_len < padto) { + count += p_len; + continue; + } + + d_off = skb->len - count; + vaddr = kmap_atomic(p); + memset(vaddr + p_off + d_off, 0, padding_len); + kunmap_atomic(vaddr); + count += d_off + padding_len; + skb->len = padto; + break; + } + } else { + /* messy case, padding split between pages */ + skb_frag_foreach_page(frag, f_off, f_len, p, p_off, + p_len, copied) { + if (count + p_len < skb->len) { + count += p_len; + continue; + } + + /* need to add padding into next page */ + if (count + p_len < padto) { + d_off = skb->len - count; + d_len = p_len - d_off; + + vaddr = kmap_atomic(p); + memset(vaddr + p_off + d_off, 0, d_len); + kunmap_atomic(vaddr); + + count += p_len; + padding_len -= d_len; + skb->len += d_len; + continue; + } + + d_off = (count < skb->len) ? skb->len - count : 0; + vaddr = kmap_atomic(p); + memset(vaddr + p_off + d_off, 0, padding_len); + kunmap_atomic(vaddr); + count += d_off + padding_len; + skb->len += padding_len; + } + } + + if (skb->len == padto) + break; + } + WARN_ON(skb->len != padto); + + return 0; +} + /* Pass an outgoing packet socket buffer to the endpoint driver. */ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, int type, struct sockaddr_qrtr *from, @@ -690,10 +779,15 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, hdr->confirm_rx = !!confirm_rx; qrtr_log_tx_msg(node, hdr, skb); - rc = skb_put_padto(skb, ALIGN(len, 4) + sizeof(*hdr)); + /* word align the data and pad with 0s */ + if (skb_is_nonlinear(skb)) + rc = qrtr_pad_word_pskb(skb); + else + rc = skb_put_padto(skb, ALIGN(len, 4) + sizeof(*hdr)); + if (rc) pr_err("%s: failed to pad size %lu to %lu rc:%d\n", __func__, - len, ALIGN(len, 4) + sizeof(*hdr), rc); + skb->len, ALIGN(skb->len, 4), rc); if (!rc) { mutex_lock(&node->ep_lock); @@ -1681,6 +1775,8 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) struct qrtr_node *node; struct qrtr_node *srv_node; struct sk_buff *skb; + int pdata_len = 0; + int data_len = 0; size_t plen; u32 type; int rc; @@ -1742,8 +1838,17 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) } plen = (len + 3) & ~3; - skb = sock_alloc_send_skb(sk, plen + QRTR_HDR_MAX_SIZE, - msg->msg_flags & MSG_DONTWAIT, &rc); + if (plen > SKB_MAX_ALLOC) { + data_len = min_t(size_t, + plen - SKB_MAX_ALLOC, + MAX_SKB_FRAGS * PAGE_SIZE); + pdata_len = PAGE_ALIGN(data_len); + + BUILD_BUG_ON(SKB_MAX_ALLOC < PAGE_SIZE); + } + skb = sock_alloc_send_pskb(sk, QRTR_HDR_MAX_SIZE + (plen - data_len), + pdata_len, msg->msg_flags & MSG_DONTWAIT, + &rc, PAGE_ALLOC_COSTLY_ORDER); if (!skb) { rc = -ENOMEM; goto out_node; @@ -1751,7 +1856,13 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) skb_reserve(skb, QRTR_HDR_MAX_SIZE); - rc = memcpy_from_msg(skb_put(skb, len), msg, len); + /* len is used by the enqueue functions and should remain accurate + * regardless of padding or allocation size + */ + skb_put(skb, len - data_len); + skb->data_len = data_len; + skb->len = len; + rc = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len); if (rc) { kfree_skb(skb); goto out_node; From 9c15def13e76daa373df55fe3b55494ed4560c6b Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Mon, 9 May 2022 19:45:41 -0700 Subject: [PATCH 11/13] net: qrtr: gunyah: Add support for paged skb sends Add support to handle paged skbs from the qrtr code layer. Neither the pskb or the sg APIs support copying into io memory. Add a helper in qrtr gunyah to copy from scatterlists into the fifo io memory. Not all skbs coming from qrtr core will be paged skbs, keep support for linear buffer copies in the tx path. Change-Id: Ifccab77e45ec70fd4c7bad92b06497c65403461e Signed-off-by: Chris Lew --- net/qrtr/gunyah.c | 78 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/net/qrtr/gunyah.c b/net/qrtr/gunyah.c index 38118890eed6..7f5a1898d3e2 100644 --- a/net/qrtr/gunyah.c +++ b/net/qrtr/gunyah.c @@ -219,6 +219,60 @@ static void gunyah_tx_write(struct gunyah_pipe *pipe, const void *data, *pipe->head = cpu_to_le32(head); } +static size_t gunyah_sg_copy_toio(struct scatterlist *sg, unsigned int nents, + void *buf, size_t buflen, off_t skip) +{ + unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG; + struct sg_mapping_iter miter; + unsigned int offset = 0; + + sg_miter_start(&miter, sg, nents, sg_flags); + + if (!sg_miter_skip(&miter, skip)) + return 0; + + while ((offset < buflen) && sg_miter_next(&miter)) { + unsigned int len; + + len = min(miter.length, buflen - offset); + memcpy_toio(buf + offset, miter.addr, len); + offset += len; + } + + sg_miter_stop(&miter); + + return offset; +} + +static void gunyah_sg_write(struct gunyah_pipe *pipe, struct scatterlist *sg, + int offset, size_t count) +{ + size_t len; + u32 head; + int rc = 0; + + head = le32_to_cpu(*pipe->head); + + len = min_t(size_t, count, pipe->length - head); + if (len) { + rc = gunyah_sg_copy_toio(sg, sg_nents(sg), pipe->fifo + head, + len, offset); + offset += rc; + } + + if (len != count) + rc = gunyah_sg_copy_toio(sg, sg_nents(sg), pipe->fifo, + count - len, offset); + + head += count; + if (head >= pipe->length) + head -= pipe->length; + + smp_wmb(); + + *pipe->head = cpu_to_le32(head); +} + static void gunyah_set_tx_notify(struct qrtr_gunyah_dev *qdev) { *qdev->tx_pipe.read_notify = cpu_to_le32(1); @@ -249,17 +303,10 @@ static int qrtr_gunyah_send(struct qrtr_endpoint *ep, struct sk_buff *skb) int chunk_size; int left_size; int offset; - int rc; qdev = container_of(ep, struct qrtr_gunyah_dev, ep); - rc = skb_linearize(skb); - if (rc) { - kfree_skb(skb); - return rc; - } - left_size = skb->len; offset = 0; while (left_size > 0) { @@ -273,7 +320,22 @@ static int qrtr_gunyah_send(struct qrtr_endpoint *ep, struct sk_buff *skb) else chunk_size = left_size; - gunyah_tx_write(&qdev->tx_pipe, skb->data + offset, chunk_size); + if (skb_is_nonlinear(skb)) { + struct scatterlist sg[MAX_SKB_FRAGS + 1]; + + sg_init_table(sg, skb_shinfo(skb)->nr_frags + 1); + rc = skb_to_sgvec(skb, sg, 0, skb->len); + if (rc < 0) { + pr_err("failed skb_to_sgvec rc:%d\n", rc); + break; + } + gunyah_sg_write(&qdev->tx_pipe, sg, offset, + chunk_size); + } else { + gunyah_tx_write(&qdev->tx_pipe, skb->data + offset, + chunk_size); + } + offset += chunk_size; left_size -= chunk_size; From ecaafddf5eb65df70cc26d3e0a69fc8a4e449658 Mon Sep 17 00:00:00 2001 From: Sarannya S Date: Wed, 1 Jun 2022 15:33:10 +0530 Subject: [PATCH 12/13] net: qrtr: Use xa_load in qrtr_get_service_id In qrtr_get_service_id, use xa_load instead of node_get to check if the node exists or not. Calling node_get from interrupt context can cause potential deadlock since it calls into xa_store to allocate the node if it does not exist. Change-Id: Ida9f7a113417f0d184c0903004d94dd2eca6c472 Signed-off-by: Sarannya S --- net/qrtr/ns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index fb248f4fb934..8f40c3fedf22 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -105,7 +105,7 @@ int qrtr_get_service_id(unsigned int node_id, unsigned int port_id) struct qrtr_node *node; unsigned long index; - node = node_get(node_id); + node = xa_load(&nodes, node_id); if (!node) return -EINVAL; From 482c42cf375f9471bb321f1b7826222f04359f8d Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 6 Jul 2022 20:46:02 -0700 Subject: [PATCH 13/13] net: qrtr: Update data_len when padding large skbs When the skb is greater than 16kb and needs to by padded, qrtr attempts to manually pad the trailing bytes to be zeroed out and word aligned. The padding of the page was happening correctly, but the bookkeeping on the skb was not accurate. When skb->len was updated to account for the new padding but skb->data_len was not, this made all skb functions think the linear portion (skb->len - skb_data_len) was longer than it actually was. This caused a pattern where skb_copy_bits() would copy out of bounds on the linear section and shift the trailing bits into the padded section. Before padding without updating data_len. [ 104.825620] qrtr: 00003e80: 66 d3 a0 e1 b6 04 e4 43 b7 aa f0 40 fb eb 38 dc [ 104.825622] qrtr: 00003e90: f1 91 85 e5 17 26 2a a2 11 49 bc cc bd f3 d3 23 [ 104.825624] qrtr: 00003ea0: c6. Adter padding without updating data_len. [ 104.860041] qrtr: 00003e80: 66 d3 a0 e1 b6 04 e4 43 b7 aa f0 40 fb eb 38 dc [ 104.860042] qrtr: 00003e90: f1 91 85 e5 17 26 2a a2 11 49 bc cc bd 00 00 00 [ 104.860043] qrtr: 00003ea0: f3 d3 23 c6. Change-Id: I6c1c944ecf696360ada263046b0e0af1bfdeb505 Signed-off-by: Chris Lew --- net/qrtr/af_qrtr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 9c04326bf48b..ced6ec7b4f4b 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -688,6 +688,7 @@ static int qrtr_pad_word_pskb(struct sk_buff *skb) kunmap_atomic(vaddr); count += d_off + padding_len; skb->len = padto; + skb->data_len += padding_len; break; } } else { @@ -711,6 +712,7 @@ static int qrtr_pad_word_pskb(struct sk_buff *skb) count += p_len; padding_len -= d_len; skb->len += d_len; + skb->data_len += padding_len; continue; } @@ -720,6 +722,7 @@ static int qrtr_pad_word_pskb(struct sk_buff *skb) kunmap_atomic(vaddr); count += d_off + padding_len; skb->len += padding_len; + skb->data_len += padding_len; } }