net: qrtr: Release distant nodes along the bridge node
authorLoic Poulain <loic.poulain@linaro.org>
Fri, 6 Nov 2020 17:33:30 +0000 (18:33 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 11 Nov 2020 23:29:34 +0000 (15:29 -0800)
Distant QRTR nodes can be accessed via an other node that acts as
a bridge. When the a QRTR endpoint associated to a bridge node is
released, all the linked distant nodes should also be released.

This patch fixes endpoint release by:
- Submitting QRTR BYE message locally on behalf of all the nodes
accessible through the endpoint.
- Removing all the routable node IDs from radix tree pointing to
the released node endpoint.

Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/qrtr/qrtr.c

index d4305de..f4ab3ca 100644 (file)
@@ -171,8 +171,13 @@ static void __qrtr_node_release(struct kref *kref)
        void __rcu **slot;
 
        spin_lock_irqsave(&qrtr_nodes_lock, flags);
-       if (node->nid != QRTR_EP_NID_AUTO)
-               radix_tree_delete(&qrtr_nodes, node->nid);
+       /* If the node is a bridge for other nodes, there are possibly
+        * multiple entries pointing to our released node, delete them all.
+        */
+       radix_tree_for_each_slot(slot, &qrtr_nodes, &iter, 0) {
+               if (*slot == node)
+                       radix_tree_iter_delete(&qrtr_nodes, &iter, slot);
+       }
        spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
 
        list_del(&node->item);
@@ -602,6 +607,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep)
        struct qrtr_ctrl_pkt *pkt;
        struct qrtr_tx_flow *flow;
        struct sk_buff *skb;
+       unsigned long flags;
        void __rcu **slot;
 
        mutex_lock(&node->ep_lock);
@@ -609,11 +615,18 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep)
        mutex_unlock(&node->ep_lock);
 
        /* Notify the local controller about the event */
-       skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL);
-       if (skb) {
-               pkt->cmd = cpu_to_le32(QRTR_TYPE_BYE);
-               qrtr_local_enqueue(NULL, skb, QRTR_TYPE_BYE, &src, &dst);
+       spin_lock_irqsave(&qrtr_nodes_lock, flags);
+       radix_tree_for_each_slot(slot, &qrtr_nodes, &iter, 0) {
+               if (*slot != node)
+                       continue;
+               src.sq_node = iter.index;
+               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);
+               }
        }
+       spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
 
        /* Wake up any transmitters waiting for resume-tx from the node */
        mutex_lock(&node->qrtr_tx_lock);