Index: uspace/srv/net/tcp/sock.c
===================================================================
--- uspace/srv/net/tcp/sock.c	(revision 0d520a20c421e167bdbda18118a803c689ad9fd2)
+++ uspace/srv/net/tcp/sock.c	(revision 7c912b6fc05a13f4e6a53dd5ad6dd9249406ea85)
@@ -42,5 +42,4 @@
 #include <ipc/services.h>
 #include <ipc/socket.h>
-#include <net/modules.h>
 #include <net/socket.h>
 #include <ns.h>
@@ -52,6 +51,4 @@
 #include "ucall.h"
 
-#define FRAGMENT_SIZE 1024
-
 #define MAX_BACKLOG 128
 
@@ -67,4 +64,5 @@
 static void tcp_sock_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
 static void tcp_sock_cstate_cb(tcp_conn_t *conn, void *arg);
+static int tcp_sock_recv_fibril(void *arg);
 
 int tcp_sock_init(void)
@@ -98,5 +96,5 @@
 	async_exch_t *exch = async_exchange_begin(sock_core->sess);
 	async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t)sock_core->socket_id,
-	    FRAGMENT_SIZE, 0, 0, 1);
+	    TCP_SOCK_FRAGMENT_SIZE, 0, 0, 1);
 	async_exchange_end(exch);
 }
@@ -107,12 +105,65 @@
 	async_exch_t *exch = async_exchange_begin(lsock_core->sess);
 	async_msg_5(exch, NET_SOCKET_ACCEPTED, (sysarg_t)lsock_core->socket_id,
-	    FRAGMENT_SIZE, 0, 0, 0);
+	    TCP_SOCK_FRAGMENT_SIZE, 0, 0, 0);
 	async_exchange_end(exch);
 }
 
+static int tcp_sock_create(tcp_client_t *client, tcp_sockdata_t **rsock)
+{
+	tcp_sockdata_t *sock;
+
+	log_msg(LVL_DEBUG, "tcp_sock_create()");
+	*rsock = NULL;
+
+	sock = calloc(sizeof(tcp_sockdata_t), 1);
+	if (sock == NULL)
+		return ENOMEM;
+
+	fibril_mutex_initialize(&sock->lock);
+	sock->client = client;
+
+	sock->recv_buffer_used = 0;
+	sock->recv_error = TCP_EOK;
+	fibril_mutex_initialize(&sock->recv_buffer_lock);
+	fibril_condvar_initialize(&sock->recv_buffer_cv);
+	list_initialize(&sock->ready);
+
+	*rsock = sock;
+	return EOK;
+}
+
+static void tcp_sock_uncreate(tcp_sockdata_t *sock)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_uncreate()");
+	free(sock);
+}
+
+static int tcp_sock_finish_setup(tcp_sockdata_t *sock, int *sock_id)
+{
+	socket_core_t *sock_core;
+	int rc;
+
+	log_msg(LVL_DEBUG, "tcp_sock_finish_setup()");
+
+	sock->recv_fibril = fibril_create(tcp_sock_recv_fibril, sock);
+	if (sock->recv_fibril == 0)
+		return ENOMEM;
+
+	rc = socket_create(&sock->client->sockets, sock->client->sess,
+	    sock, sock_id);
+
+	if (rc != EOK)
+		return rc;
+
+	sock_core = socket_cores_find(&sock->client->sockets, *sock_id);
+	assert(sock_core != NULL);
+	sock->sock_core = sock_core;
+
+	return EOK;
+}
+
 static void tcp_sock_socket(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
 {
 	tcp_sockdata_t *sock;
-	socket_core_t *sock_core;
 	int sock_id;
 	int rc;
@@ -120,34 +171,30 @@
 
 	log_msg(LVL_DEBUG, "tcp_sock_socket()");
-	sock = calloc(sizeof(tcp_sockdata_t), 1);
-	if (sock == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	fibril_mutex_initialize(&sock->lock);
-	sock->client = client;
+
+	rc = tcp_sock_create(client, &sock);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
 	sock->laddr.ipv4 = TCP_IPV4_ANY;
 	sock->lconn = NULL;
 	sock->backlog = 0;
-	list_initialize(&sock->ready);
 
 	sock_id = SOCKET_GET_SOCKET_ID(call);
-	rc = socket_create(&client->sockets, client->sess, sock, &sock_id);
+	rc = tcp_sock_finish_setup(sock, &sock_id);
 	if (rc != EOK) {
+		tcp_sock_uncreate(sock);
 		async_answer_0(callid, rc);
 		return;
 	}
 
-	sock_core = socket_cores_find(&client->sockets, sock_id);
-	assert(sock_core != NULL);
-	sock->sock_core = sock_core;
-
-	refresh_answer(&answer, NULL);
 	SOCKET_SET_SOCKET_ID(answer, sock_id);
 
-	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, TCP_SOCK_FRAGMENT_SIZE);
 	SOCKET_SET_HEADER_SIZE(answer, sizeof(tcp_header_t));
-	answer_call(callid, EOK, &answer, 3);
+	
+	async_answer_3(callid, EOK, IPC_GET_ARG1(answer),
+	    IPC_GET_ARG2(answer), IPC_GET_ARG3(answer));
 }
 
@@ -361,9 +408,8 @@
 	}
 
+	if (rc == EOK)
+		fibril_add_ready(socket->recv_fibril);
+
 	async_answer_0(callid, rc);
-
-	/* Push one fragment notification to client's queue */
-	tcp_sock_notify_data(sock_core);
-	log_msg(LVL_DEBUG, "tcp_sock_connect(): notify conn\n");
 }
 
@@ -374,5 +420,4 @@
 	int asock_id;
 	socket_core_t *sock_core;
-	socket_core_t *asock_core;
 	tcp_sockdata_t *socket;
 	tcp_sockdata_t *asocket;
@@ -444,39 +489,36 @@
 	/* Allocate socket for accepted connection */
 
-	log_msg(LVL_DEBUG, "tcp_sock_accept(): allocate asocket\n");
-	asocket = calloc(sizeof(tcp_sockdata_t), 1);
-	if (asocket == NULL) {
-		fibril_mutex_unlock(&socket->lock);
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	fibril_mutex_initialize(&asocket->lock);
-	asocket->client = client;
+	rc = tcp_sock_create(client, &asocket);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&socket->lock);
+		async_answer_0(callid, rc);
+		return;
+	}
+
 	asocket->conn = conn;
 	log_msg(LVL_DEBUG, "tcp_sock_accept():create asocket\n");
 
-	rc = socket_create(&client->sockets, client->sess, asocket, &asock_id);
+	rc = tcp_sock_finish_setup(asocket, &asock_id);
 	if (rc != EOK) {
+		tcp_sock_uncreate(asocket);
 		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, rc);
 		return;
 	}
+
+	fibril_add_ready(asocket->recv_fibril);
+
 	log_msg(LVL_DEBUG, "tcp_sock_accept(): find acore\n");
 
-	asock_core = socket_cores_find(&client->sockets, asock_id);
-	assert(asock_core != NULL);
-
-	refresh_answer(&answer, NULL);
-
-	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, TCP_SOCK_FRAGMENT_SIZE);
 	SOCKET_SET_SOCKET_ID(answer, asock_id);
 	SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(struct sockaddr_in));
-
-	answer_call(callid, asock_core->socket_id, &answer, 3);
-
+	
+	async_answer_3(callid, asocket->sock_core->socket_id,
+	    IPC_GET_ARG1(answer), IPC_GET_ARG2(answer),
+	    IPC_GET_ARG3(answer));
+	
 	/* Push one fragment notification to client's queue */
 	log_msg(LVL_DEBUG, "tcp_sock_accept(): notify data\n");
-	tcp_sock_notify_data(asock_core);
 	fibril_mutex_unlock(&socket->lock);
 }
@@ -492,5 +534,5 @@
 	ipc_callid_t wcallid;
 	size_t length;
-	uint8_t buffer[FRAGMENT_SIZE];
+	uint8_t buffer[TCP_SOCK_FRAGMENT_SIZE];
 	tcp_error_t trc;
 	int rc;
@@ -523,6 +565,6 @@
 		}
 
-		if (length > FRAGMENT_SIZE)
-			length = FRAGMENT_SIZE;
+		if (length > TCP_SOCK_FRAGMENT_SIZE)
+			length = TCP_SOCK_FRAGMENT_SIZE;
 
 		rc = async_data_write_finalize(wcallid, buffer, length);
@@ -559,7 +601,8 @@
 	}
 
-	refresh_answer(&answer, NULL);
-	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
-	answer_call(callid, EOK, &answer, 2);
+	IPC_SET_ARG1(answer, 0);
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, TCP_SOCK_FRAGMENT_SIZE);
+	async_answer_2(callid, EOK, IPC_GET_ARG1(answer),
+	    IPC_GET_ARG2(answer));
 	fibril_mutex_unlock(&socket->lock);
 }
@@ -580,8 +623,5 @@
 	ipc_call_t answer;
 	ipc_callid_t rcallid;
-	uint8_t buffer[FRAGMENT_SIZE];
 	size_t data_len;
-	xflags_t xflags;
-	tcp_error_t trc;
 	struct sockaddr_in addr;
 	tcp_sock_t *rsock;
@@ -610,9 +650,18 @@
 	(void)flags;
 
-	trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE, &data_len,
-	    &xflags);
-	log_msg(LVL_DEBUG, "**** tcp_uc_receive done");
-
-	switch (trc) {
+	log_msg(LVL_DEBUG, "tcp_sock_recvfrom(): lock recv_buffer_lock");
+	fibril_mutex_lock(&socket->recv_buffer_lock);
+	while (socket->recv_buffer_used == 0 && socket->recv_error == TCP_EOK) {
+		log_msg(LVL_DEBUG, "wait for recv_buffer_cv + recv_buffer_used != 0");
+		fibril_condvar_wait(&socket->recv_buffer_cv,
+		    &socket->recv_buffer_lock);
+	}
+
+	log_msg(LVL_DEBUG, "Got data in sock recv_buffer");
+
+	data_len = socket->recv_buffer_used;
+	rc = socket->recv_error;
+
+	switch (socket->recv_error) {
 	case TCP_EOK:
 		rc = EOK;
@@ -629,6 +678,7 @@
 	}
 
-	log_msg(LVL_DEBUG, "**** tcp_uc_receive -> %d", rc);
+	log_msg(LVL_DEBUG, "**** recv result -> %d", rc);
 	if (rc != EOK) {
+		fibril_mutex_unlock(&socket->recv_buffer_lock);
 		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, rc);
@@ -645,4 +695,5 @@
 		log_msg(LVL_DEBUG, "addr read receive");
 		if (!async_data_read_receive(&rcallid, &addr_length)) {
+			fibril_mutex_unlock(&socket->recv_buffer_lock);
 			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EINVAL);
@@ -656,4 +707,5 @@
 		rc = async_data_read_finalize(rcallid, &addr, addr_length);
 		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->recv_buffer_lock);
 			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EINVAL);
@@ -664,4 +716,5 @@
 	log_msg(LVL_DEBUG, "data read receive");
 	if (!async_data_read_receive(&rcallid, &length)) {
+		fibril_mutex_unlock(&socket->recv_buffer_lock);
 		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, EINVAL);
@@ -673,5 +726,16 @@
 
 	log_msg(LVL_DEBUG, "data read finalize");
-	rc = async_data_read_finalize(rcallid, buffer, length);
+	rc = async_data_read_finalize(rcallid, socket->recv_buffer, length);
+
+	socket->recv_buffer_used -= length;
+	log_msg(LVL_DEBUG, "tcp_sock_recvfrom: %zu left in buffer",
+	    socket->recv_buffer_used);
+	if (socket->recv_buffer_used > 0) {
+		memmove(socket->recv_buffer, socket->recv_buffer + length,
+		    socket->recv_buffer_used);
+		tcp_sock_notify_data(socket->sock_core);
+	}
+
+	fibril_condvar_broadcast(&socket->recv_buffer_cv);
 
 	if (length < data_len && rc == EOK)
@@ -679,8 +743,7 @@
 
 	SOCKET_SET_READ_DATA_LENGTH(answer, length);
-	answer_call(callid, EOK, &answer, 1);
-
-	/* Push one fragment notification to client's queue */
-	tcp_sock_notify_data(sock_core);
+	async_answer_1(callid, EOK, IPC_GET_ARG1(answer));
+
+	fibril_mutex_unlock(&socket->recv_buffer_lock);
 	fibril_mutex_unlock(&socket->lock);
 }
@@ -693,7 +756,4 @@
 	tcp_error_t trc;
 	int rc;
-	uint8_t buffer[FRAGMENT_SIZE];
-	size_t data_len;
-	xflags_t xflags;
 
 	log_msg(LVL_DEBUG, "tcp_sock_close()");
@@ -716,12 +776,4 @@
 			return;
 		}
-
-		/* Drain incoming data. This should really be done in the background. */
-		do {
-			trc = tcp_uc_receive(socket->conn, buffer,
-			    FRAGMENT_SIZE, &data_len, &xflags);
-		} while (trc == TCP_EOK);
-
-		tcp_uc_delete(socket->conn);
 	}
 
@@ -775,4 +827,44 @@
 	tcp_sock_notify_aconn(socket->sock_core);
 	fibril_mutex_unlock(&socket->lock);
+}
+
+static int tcp_sock_recv_fibril(void *arg)
+{
+	tcp_sockdata_t *sock = (tcp_sockdata_t *)arg;
+	size_t data_len;
+	xflags_t xflags;
+	tcp_error_t trc;
+
+	log_msg(LVL_DEBUG, "tcp_sock_recv_fibril()");
+
+	while (true) {
+		log_msg(LVL_DEBUG, "call tcp_uc_receive()");
+		fibril_mutex_lock(&sock->recv_buffer_lock);
+		while (sock->recv_buffer_used != 0)
+			fibril_condvar_wait(&sock->recv_buffer_cv,
+			    &sock->recv_buffer_lock);
+
+		trc = tcp_uc_receive(sock->conn, sock->recv_buffer,
+		    TCP_SOCK_FRAGMENT_SIZE, &data_len, &xflags);
+
+		if (trc != TCP_EOK) {
+			sock->recv_error = trc;
+			fibril_condvar_broadcast(&sock->recv_buffer_cv);
+			fibril_mutex_unlock(&sock->recv_buffer_lock);
+			tcp_sock_notify_data(sock->sock_core);
+			break;
+		}
+
+		log_msg(LVL_DEBUG, "got data - broadcast recv_buffer_cv");
+
+		sock->recv_buffer_used = data_len;
+		fibril_condvar_broadcast(&sock->recv_buffer_cv);
+		fibril_mutex_unlock(&sock->recv_buffer_lock);
+		tcp_sock_notify_data(sock->sock_core);
+	}
+
+	tcp_uc_delete(sock->conn);
+
+	return 0;
 }
 
