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);