tipc: fix netns refcnt leak
[firefly-linux-kernel-4.4.55.git] / net / tipc / server.c
index eadd4ed459051ddc776a82f4f2fcc9fe34ab89cd..a57c8407cbf30822b1f92e5f25b7bc3c09ffe9be 100644 (file)
 #include "core.h"
 #include "socket.h"
 #include <net/sock.h>
+#include <linux/module.h>
 
 /* Number of messages to send before rescheduling */
 #define MAX_SEND_MSG_COUNT     25
 #define MAX_RECV_MSG_COUNT     25
 #define CF_CONNECTED           1
+#define CF_SERVER              2
 
 #define sock2con(x) ((struct tipc_conn *)(x)->sk_user_data)
 
@@ -88,9 +90,16 @@ static void tipc_clean_outqueues(struct tipc_conn *con);
 static void tipc_conn_kref_release(struct kref *kref)
 {
        struct tipc_conn *con = container_of(kref, struct tipc_conn, kref);
+       struct socket *sock = con->sock;
+       struct sock *sk;
 
-       if (con->sock) {
-               tipc_sock_release_local(con->sock);
+       if (sock) {
+               sk = sock->sk;
+               if (test_bit(CF_SERVER, &con->flags)) {
+                       __module_get(sock->ops->owner);
+                       __module_get(sk->sk_prot_creator->owner);
+               }
+               sk_release_kernel(sk);
                con->sock = NULL;
        }
 
@@ -281,7 +290,7 @@ static int tipc_accept_from_sock(struct tipc_conn *con)
        struct tipc_conn *newcon;
        int ret;
 
-       ret = tipc_sock_accept_local(sock, &newsock, O_NONBLOCK);
+       ret = kernel_accept(sock, &newsock, O_NONBLOCK);
        if (ret < 0)
                return ret;
 
@@ -309,9 +318,12 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con)
        struct socket *sock = NULL;
        int ret;
 
-       ret = tipc_sock_create_local(s->net, s->type, &sock);
+       ret = sock_create_kern(AF_TIPC, SOCK_SEQPACKET, 0, &sock);
        if (ret < 0)
                return NULL;
+
+       sk_change_net(sock->sk, s->net);
+
        ret = kernel_setsockopt(sock, SOL_TIPC, TIPC_IMPORTANCE,
                                (char *)&s->imp, sizeof(s->imp));
        if (ret < 0)
@@ -337,11 +349,31 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con)
                pr_err("Unknown socket type %d\n", s->type);
                goto create_err;
        }
+
+       /* As server's listening socket owner and creator is the same module,
+        * we have to decrease TIPC module reference count to guarantee that
+        * it remains zero after the server socket is created, otherwise,
+        * executing "rmmod" command is unable to make TIPC module deleted
+        * after TIPC module is inserted successfully.
+        *
+        * However, the reference count is ever increased twice in
+        * sock_create_kern(): one is to increase the reference count of owner
+        * of TIPC socket's proto_ops struct; another is to increment the
+        * reference count of owner of TIPC proto struct. Therefore, we must
+        * decrement the module reference count twice to ensure that it keeps
+        * zero after server's listening socket is created. Of course, we
+        * must bump the module reference count twice as well before the socket
+        * is closed.
+        */
+       module_put(sock->ops->owner);
+       module_put(sock->sk->sk_prot_creator->owner);
+       set_bit(CF_SERVER, &con->flags);
+
        return sock;
 
 create_err:
-       sock_release(sock);
-       con->sock = NULL;
+       kernel_sock_shutdown(sock, SHUT_RDWR);
+       sk_release_kernel(sock->sk);
        return NULL;
 }