net/uip: Avoid deadlock in uip_tcp_socket_free()

The function uip_tcp_socket_free() is called with the sk lock held, but
then goes on to call uip_tcp_socket_close() which attempts to aquire the
lock a second time, triggering a deadlock if there are outstanding TCP
connections.

Rename the existing uip_tcp_socket_close() to a _locked variety and
removing the locking from it. Add a new uip_tcp_socket_close() which
takes the lock and calls the _locked variety.

Fixes: d87b503f4d6e ("net/uip: Add exit function")
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
diff --git a/net/uip/tcp.c b/net/uip/tcp.c
index 8e0ad52..42e6e99 100644
--- a/net/uip/tcp.c
+++ b/net/uip/tcp.c
@@ -6,7 +6,7 @@
 #include <linux/list.h>
 #include <arpa/inet.h>
 
-static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
+static int uip_tcp_socket_close_locked(struct uip_tcp_socket *sk, int how)
 {
 	shutdown(sk->fd, how);
 
@@ -14,9 +14,7 @@
 		shutdown(sk->fd, SHUT_RDWR);
 		close(sk->fd);
 
-		mutex_lock(sk->lock);
 		list_del(&sk->list);
-		mutex_unlock(sk->lock);
 
 		free(sk->buf);
 		free(sk);
@@ -25,6 +23,17 @@
 	return 0;
 }
 
+static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
+{
+	int ret;
+
+	mutex_lock(sk->lock);
+	ret = uip_tcp_socket_close_locked(sk, how);
+	mutex_unlock(sk->lock);
+
+	return ret;
+}
+
 static struct uip_tcp_socket *uip_tcp_socket_find(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
 {
 	struct list_head *sk_head;
@@ -110,7 +119,7 @@
 	}
 
 	sk->write_done = sk->read_done = 1;
-	uip_tcp_socket_close(sk, SHUT_RDWR);
+	uip_tcp_socket_close_locked(sk, SHUT_RDWR);
 }
 
 static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)