]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/vmw_vsock/af_vsock.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / net / vmw_vsock / af_vsock.c
index c9473d698525a3862d6f8eda3f149b9934171795..ca279a14bbccb345cc0dfc01c9f3bc10857c0de0 100644 (file)
 #include <linux/mutex.h>
 #include <linux/net.h>
 #include <linux/poll.h>
+#include <linux/random.h>
 #include <linux/skbuff.h>
 #include <linux/smp.h>
 #include <linux/socket.h>
@@ -281,7 +282,8 @@ EXPORT_SYMBOL_GPL(vsock_insert_connected);
 void vsock_remove_bound(struct vsock_sock *vsk)
 {
        spin_lock_bh(&vsock_table_lock);
-       __vsock_remove_bound(vsk);
+       if (__vsock_in_bound_table(vsk))
+               __vsock_remove_bound(vsk);
        spin_unlock_bh(&vsock_table_lock);
 }
 EXPORT_SYMBOL_GPL(vsock_remove_bound);
@@ -289,7 +291,8 @@ EXPORT_SYMBOL_GPL(vsock_remove_bound);
 void vsock_remove_connected(struct vsock_sock *vsk)
 {
        spin_lock_bh(&vsock_table_lock);
-       __vsock_remove_connected(vsk);
+       if (__vsock_in_connected_table(vsk))
+               __vsock_remove_connected(vsk);
        spin_unlock_bh(&vsock_table_lock);
 }
 EXPORT_SYMBOL_GPL(vsock_remove_connected);
@@ -325,35 +328,10 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
 }
 EXPORT_SYMBOL_GPL(vsock_find_connected_socket);
 
-static bool vsock_in_bound_table(struct vsock_sock *vsk)
-{
-       bool ret;
-
-       spin_lock_bh(&vsock_table_lock);
-       ret = __vsock_in_bound_table(vsk);
-       spin_unlock_bh(&vsock_table_lock);
-
-       return ret;
-}
-
-static bool vsock_in_connected_table(struct vsock_sock *vsk)
-{
-       bool ret;
-
-       spin_lock_bh(&vsock_table_lock);
-       ret = __vsock_in_connected_table(vsk);
-       spin_unlock_bh(&vsock_table_lock);
-
-       return ret;
-}
-
 void vsock_remove_sock(struct vsock_sock *vsk)
 {
-       if (vsock_in_bound_table(vsk))
-               vsock_remove_bound(vsk);
-
-       if (vsock_in_connected_table(vsk))
-               vsock_remove_connected(vsk);
+       vsock_remove_bound(vsk);
+       vsock_remove_connected(vsk);
 }
 EXPORT_SYMBOL_GPL(vsock_remove_sock);
 
@@ -451,14 +429,14 @@ static int vsock_send_shutdown(struct sock *sk, int mode)
        return transport->shutdown(vsock_sk(sk), mode);
 }
 
-void vsock_pending_work(struct work_struct *work)
+static void vsock_pending_work(struct work_struct *work)
 {
        struct sock *sk;
        struct sock *listener;
        struct vsock_sock *vsk;
        bool cleanup;
 
-       vsk = container_of(work, struct vsock_sock, dwork.work);
+       vsk = container_of(work, struct vsock_sock, pending_work.work);
        sk = sk_vsock(vsk);
        listener = vsk->listener;
        cleanup = true;
@@ -484,8 +462,7 @@ void vsock_pending_work(struct work_struct *work)
         * incoming packets can't find this socket, and to reduce the reference
         * count.
         */
-       if (vsock_in_connected_table(vsk))
-               vsock_remove_connected(vsk);
+       vsock_remove_connected(vsk);
 
        sk->sk_state = TCP_CLOSE;
 
@@ -498,16 +475,19 @@ out:
        sock_put(sk);
        sock_put(listener);
 }
-EXPORT_SYMBOL_GPL(vsock_pending_work);
 
 /**** SOCKET OPERATIONS ****/
 
 static int __vsock_bind_stream(struct vsock_sock *vsk,
                               struct sockaddr_vm *addr)
 {
-       static u32 port = LAST_RESERVED_PORT + 1;
+       static u32 port = 0;
        struct sockaddr_vm new_addr;
 
+       if (!port)
+               port = LAST_RESERVED_PORT + 1 +
+                       prandom_u32_max(U32_MAX - LAST_RESERVED_PORT);
+
        vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port);
 
        if (addr->svm_port == VMADDR_PORT_ANY) {
@@ -597,6 +577,8 @@ static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr)
        return retval;
 }
 
+static void vsock_connect_timeout(struct work_struct *work);
+
 struct sock *__vsock_create(struct net *net,
                            struct socket *sock,
                            struct sock *parent,
@@ -638,6 +620,8 @@ struct sock *__vsock_create(struct net *net,
        vsk->sent_request = false;
        vsk->ignore_connecting_rst = false;
        vsk->peer_shutdown = 0;
+       INIT_DELAYED_WORK(&vsk->connect_work, vsock_connect_timeout);
+       INIT_DELAYED_WORK(&vsk->pending_work, vsock_pending_work);
 
        psk = parent ? vsock_sk(parent) : NULL;
        if (parent) {
@@ -662,7 +646,7 @@ struct sock *__vsock_create(struct net *net,
 }
 EXPORT_SYMBOL_GPL(__vsock_create);
 
-static void __vsock_release(struct sock *sk)
+static void __vsock_release(struct sock *sk, int level)
 {
        if (sk) {
                struct sk_buff *skb;
@@ -672,9 +656,17 @@ static void __vsock_release(struct sock *sk)
                vsk = vsock_sk(sk);
                pending = NULL; /* Compiler warning. */
 
+               /* The release call is supposed to use lock_sock_nested()
+                * rather than lock_sock(), if a sock lock should be acquired.
+                */
                transport->release(vsk);
 
-               lock_sock(sk);
+               /* When "level" is SINGLE_DEPTH_NESTING, use the nested
+                * version to avoid the warning "possible recursive locking
+                * detected". When "level" is 0, lock_sock_nested(sk, level)
+                * is the same as lock_sock(sk).
+                */
+               lock_sock_nested(sk, level);
                sock_orphan(sk);
                sk->sk_shutdown = SHUTDOWN_MASK;
 
@@ -683,7 +675,7 @@ static void __vsock_release(struct sock *sk)
 
                /* Clean up any sockets that never were accepted. */
                while ((pending = vsock_dequeue_accept(sk)) != NULL) {
-                       __vsock_release(pending);
+                       __vsock_release(pending, SINGLE_DEPTH_NESTING);
                        sock_put(pending);
                }
 
@@ -732,7 +724,7 @@ EXPORT_SYMBOL_GPL(vsock_stream_has_space);
 
 static int vsock_release(struct socket *sock)
 {
-       __vsock_release(sock->sk);
+       __vsock_release(sock->sk, 0);
        sock->sk = NULL;
        sock->state = SS_FREE;
 
@@ -886,7 +878,7 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock,
                 * the queue and write as long as the socket isn't shutdown for
                 * sending.
                 */
-               if (!skb_queue_empty(&sk->sk_receive_queue) ||
+               if (!skb_queue_empty_lockless(&sk->sk_receive_queue) ||
                    (sk->sk_shutdown & RCV_SHUTDOWN)) {
                        mask |= POLLIN | POLLRDNORM;
                }
@@ -1117,7 +1109,7 @@ static void vsock_connect_timeout(struct work_struct *work)
        struct vsock_sock *vsk;
        int cancel = 0;
 
-       vsk = container_of(work, struct vsock_sock, dwork.work);
+       vsk = container_of(work, struct vsock_sock, connect_work.work);
        sk = sk_vsock(vsk);
 
        lock_sock(sk);
@@ -1221,9 +1213,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr,
                         * timeout fires.
                         */
                        sock_hold(sk);
-                       INIT_DELAYED_WORK(&vsk->dwork,
-                                         vsock_connect_timeout);
-                       schedule_delayed_work(&vsk->dwork, timeout);
+                       schedule_delayed_work(&vsk->connect_work, timeout);
 
                        /* Skip ahead to preserve error code set above. */
                        goto out_wait;
@@ -2018,7 +2008,13 @@ const struct vsock_transport *vsock_core_get_transport(void)
 }
 EXPORT_SYMBOL_GPL(vsock_core_get_transport);
 
+static void __exit vsock_exit(void)
+{
+       /* Do nothing.  This function makes this module removable. */
+}
+
 module_init(vsock_init_tables);
+module_exit(vsock_exit);
 
 MODULE_AUTHOR("VMware, Inc.");
 MODULE_DESCRIPTION("VMware Virtual Socket Family");