From: Chris Lew <c...@codeaurora.org>

A remote endpoint should not need to know when a client socket is freed
if the socket never established commnication with the endpoint. Add a
mode to keep track of which endpoints a socket communicates with.

There are three modes a socket can be in:
        INIT   - Socket has not sent anything or only local messages,
                 only send client close to local services.

        SINGLE - Socket has sent messages to a single ept, send event
                 to this single ept.

        MULTI  - Socket has sent messages to multiple epts, broadcast
                 release of this socket.

Server state changes should be broadcast throughout the system. Change
the ipc state of a port when it sends a NEW SERVER control packet. This
ensures the DEL CLIENT control packet is propagated correctly for
servers.
---
 net/qrtr/qrtr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 13 deletions(-)

diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
index d9858a1..4496b75 100644
--- a/net/qrtr/qrtr.c
+++ b/net/qrtr/qrtr.c
@@ -21,6 +21,10 @@
 #define QRTR_MIN_EPH_SOCKET 0x4000
 #define QRTR_MAX_EPH_SOCKET 0x7fff
 
+/* qrtr socket states */
+#define QRTR_STATE_MULTI       -2
+#define QRTR_STATE_INIT                -1
+
 /**
  * struct qrtr_hdr_v1 - (I|R)PCrouter packet header version 1
  * @version: protocol version
@@ -87,6 +91,8 @@ struct qrtr_sock {
        struct sock sk;
        struct sockaddr_qrtr us;
        struct sockaddr_qrtr peer;
+
+       int state;
 };
 
 static inline struct qrtr_sock *qrtr_sk(struct sock *sk)
@@ -653,29 +659,59 @@ static void qrtr_port_put(struct qrtr_sock *ipc)
        sock_put(&ipc->sk);
 }
 
-/* Remove port assignment. */
-static void qrtr_port_remove(struct qrtr_sock *ipc)
+static void qrtr_send_del_client(struct qrtr_sock *ipc)
 {
        struct qrtr_ctrl_pkt *pkt;
-       struct sk_buff *skb;
-       int port = ipc->us.sq_port;
        struct sockaddr_qrtr to;
+       struct qrtr_node *node;
+       struct sk_buff *skbn;
+       struct sk_buff *skb;
+       int type = QRTR_TYPE_DEL_CLIENT;
+
+       skb = qrtr_alloc_ctrl_packet(&pkt);
+       if (!skb)
+               return;
 
        to.sq_family = AF_QIPCRTR;
        to.sq_node = QRTR_NODE_BCAST;
        to.sq_port = QRTR_PORT_CTRL;
 
-       skb = qrtr_alloc_ctrl_packet(&pkt);
-       if (skb) {
-               pkt->cmd = cpu_to_le32(QRTR_TYPE_DEL_CLIENT);
-               pkt->client.node = cpu_to_le32(ipc->us.sq_node);
-               pkt->client.port = cpu_to_le32(ipc->us.sq_port);
+       pkt->cmd = cpu_to_le32(QRTR_TYPE_DEL_CLIENT);
+       pkt->client.node = cpu_to_le32(ipc->us.sq_node);
+       pkt->client.port = cpu_to_le32(ipc->us.sq_port);
+
+       skb_set_owner_w(skb, &ipc->sk);
 
-               skb_set_owner_w(skb, &ipc->sk);
-               qrtr_bcast_enqueue(NULL, skb, QRTR_TYPE_DEL_CLIENT, &ipc->us,
-                                  &to);
+       if (ipc->state == QRTR_STATE_MULTI) {
+               qrtr_bcast_enqueue(NULL, skb, type, &ipc->us, &to);
+               return;
+       }
+
+       if (ipc->state > QRTR_STATE_INIT) {
+               node = qrtr_node_lookup(ipc->state);
+               if (!node)
+                       goto exit;
+
+               skbn = skb_clone(skb, GFP_KERNEL);
+               if (!skbn) {
+                       qrtr_node_release(node);
+                       goto exit;
+               }
+
+               skb_set_owner_w(skbn, &ipc->sk);
+               qrtr_node_enqueue(node, skbn, type, &ipc->us, &to);
+               qrtr_node_release(node);
        }
+exit:
+       qrtr_local_enqueue(NULL, skb, type, &ipc->us, &to);
+}
 
+/* Remove port assignment. */
+static void qrtr_port_remove(struct qrtr_sock *ipc)
+{
+       int port = ipc->us.sq_port;
+
+       qrtr_send_del_client(ipc);
        if (port == QRTR_PORT_CTRL)
                port = 0;
 
@@ -941,6 +977,11 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr 
*msg, size_t len)
                        return -ECONNRESET;
                }
                enqueue_fn = qrtr_node_enqueue;
+
+               if (ipc->state > QRTR_STATE_INIT && ipc->state != node->nid)
+                       ipc->state = QRTR_STATE_MULTI;
+               else if (ipc->state == QRTR_STATE_INIT)
+                       ipc->state = node->nid;
        }
 
        plen = (len + 3) & ~3;
@@ -957,7 +998,8 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr 
*msg, size_t len)
                goto out_node;
        }
 
-       if (ipc->us.sq_port == QRTR_PORT_CTRL) {
+       if (ipc->us.sq_port == QRTR_PORT_CTRL ||
+           addr->sq_port == QRTR_PORT_CTRL) {
                if (len < 4) {
                        rc = -EINVAL;
                        kfree_skb(skb);
@@ -969,6 +1011,9 @@ 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)
+               ipc->state = QRTR_STATE_MULTI;
+
        rc = enqueue_fn(node, skb, type, &ipc->us, addr);
        if (rc >= 0)
                rc = len;
@@ -1256,6 +1301,7 @@ static int qrtr_create(struct net *net, struct socket 
*sock,
        ipc->us.sq_family = AF_QIPCRTR;
        ipc->us.sq_node = qrtr_local_nid;
        ipc->us.sq_port = 0;
+       ipc->state = QRTR_STATE_INIT;
 
        return 0;
 }
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

Reply via email to