[10/12] soccr/tcp: Restore queues using library

Submitted by Pavel Emelianov on Aug. 5, 2016, 3:02 p.m.

Details

Message ID 57A4AA84.8080706@virtuozzo.com
State Accepted
Series "Introduce library for socket C/R"
Commit e2a8d0665bb81cfdac03cbb6573dbf1bf3074872
Headers show

Commit Message

Pavel Emelianov Aug. 5, 2016, 3:02 p.m.
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
---
 criu/sk-tcp.c |  93 ++++++++--------------------------------------------
 soccr/soccr.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 soccr/soccr.h |   2 ++
 3 files changed, 118 insertions(+), 79 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/sk-tcp.c b/criu/sk-tcp.c
index 2007e52..0dd6b77 100644
--- a/criu/sk-tcp.c
+++ b/criu/sk-tcp.c
@@ -282,10 +282,9 @@  int dump_one_tcp(int fd, struct inet_sk_desc *sk)
 	return 0;
 }
 
-static int __send_tcp_queue(int sk, int queue, u32 len, struct cr_img *img)
+static int send_tcp_queue(struct libsoccr_sk *sk, struct libsoccr_sk_data *data,
+		int queue, u32 len, struct cr_img *img)
 {
-	int ret, err = -1, max_chunk;
-	int off;
 	char *buf;
 
 	buf = xmalloc(len);
@@ -295,91 +294,27 @@  static int __send_tcp_queue(int sk, int queue, u32 len, struct cr_img *img)
 	if (read_img_buf(img, buf, len) < 0)
 		goto err;
 
-	max_chunk = len;
-	off = 0;
-
-	do {
-		int chunk = len;
-
-		if (chunk > max_chunk)
-			chunk = max_chunk;
-
-		ret = send(sk, buf + off, chunk, 0);
-		if (ret <= 0) {
-			if (max_chunk > 1024) {
-				/*
-				 * Kernel not only refuses the whole chunk,
-				 * but refuses to split it into pieces too.
-				 *
-				 * When restoring recv queue in repair mode
-				 * kernel doesn't try hard and just allocates
-				 * a linear skb with the size we pass to the
-				 * system call. Thus, if the size is too big
-				 * for slab allocator, the send just fails
-				 * with ENOMEM.
-				 *
-				 * In any case -- try smaller chunk, hopefully
-				 * there's still enough memory in the system.
-				 */
-				max_chunk >>= 1;
-				continue;
-			}
-
-			pr_perror("Can't restore %d queue data (%d), want (%d:%d:%d)",
-				  queue, ret, chunk, len, max_chunk);
-			goto err;
-		}
-		off += ret;
-		len -= ret;
-	} while (len);
+	if (libsoccr_set_queue_bytes(sk, data, sizeof(*data), queue, buf))
+		goto err;
 
-	err = 0;
-err:
 	xfree(buf);
+	return 0;
 
-	return err;
-}
-
-static int send_tcp_queue(int sk, int queue, u32 len, struct cr_img *img)
-{
-	pr_debug("\tRestoring TCP %d queue data %u bytes\n", queue, len);
-
-	if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &queue, sizeof(queue)) < 0) {
-		pr_perror("Can't set repair queue");
-		return -1;
-	}
-
-	return __send_tcp_queue(sk, queue, len, img);
+err:
+	xfree(buf);
+	return -1;
 }
 
-static int restore_tcp_queues(int sk, TcpStreamEntry *tse, struct cr_img *img)
+static int restore_tcp_queues(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, struct cr_img *img)
 {
 	u32 len;
 
-	len = tse->inq_len;
-	if (len && send_tcp_queue(sk, TCP_RECV_QUEUE, len, img))
-		return -1;
-
-	/*
-	 * All data in a write buffer can be divided on two parts sent
-	 * but not yet acknowledged data and unsent data.
-	 * The TCP stack must know which data have been sent, because
-	 * acknowledgment can be received for them. These data must be
-	 * restored in repair mode.
-	 */
-	len = tse->outq_len - tse->unsq_len;
-	if (len && send_tcp_queue(sk, TCP_SEND_QUEUE, len, img))
+	len = data->inq_len;
+	if (len && send_tcp_queue(sk, data, TCP_RECV_QUEUE, len, img))
 		return -1;
 
-	/*
-	 * The second part of data have never been sent to outside, so
-	 * they can be restored without any tricks.
-	 */
-	len = tse->unsq_len;
-	tcp_repair_off(sk);
-	if (len && __send_tcp_queue(sk, TCP_SEND_QUEUE, len, img))
-		return -1;
-	if (tcp_repair_on(sk))
+	len = data->outq_len;
+	if (len && send_tcp_queue(sk, data, TCP_SEND_QUEUE, len, img))
 		return -1;
 
 	return 0;
@@ -457,7 +392,7 @@  static int restore_tcp_conn_state(int sk, struct libsoccr_sk *socr, struct inet_
 	if (restore_prepare_socket(sk))
 		goto err_c;
 
-	if (restore_tcp_queues(sk, tse, img))
+	if (restore_tcp_queues(socr, &data, img))
 		goto err_c;
 
 	if (libsoccr_set_sk_data(socr, &data, sizeof(data)))
diff --git a/soccr/soccr.c b/soccr/soccr.c
index 61a39ad..0dfdb5e 100644
--- a/soccr/soccr.c
+++ b/soccr/soccr.c
@@ -428,3 +428,105 @@  int libsoccr_set_sk_data(struct libsoccr_sk *sk,
 
 	return 0;
 }
+
+static int __send_queue(struct libsoccr_sk *sk, int queue, char *buf, __u32 len)
+{
+	int ret, err = -1, max_chunk;
+	int off;
+
+	max_chunk = len;
+	off = 0;
+
+	do {
+		int chunk = len;
+
+		if (chunk > max_chunk)
+			chunk = max_chunk;
+
+		ret = send(sk->fd, buf + off, chunk, 0);
+		if (ret <= 0) {
+			if (max_chunk > 1024) {
+				/*
+				 * Kernel not only refuses the whole chunk,
+				 * but refuses to split it into pieces too.
+				 *
+				 * When restoring recv queue in repair mode
+				 * kernel doesn't try hard and just allocates
+				 * a linear skb with the size we pass to the
+				 * system call. Thus, if the size is too big
+				 * for slab allocator, the send just fails
+				 * with ENOMEM.
+				 *
+				 * In any case -- try smaller chunk, hopefully
+				 * there's still enough memory in the system.
+				 */
+				max_chunk >>= 1;
+				continue;
+			}
+
+			loge("Can't restore %d queue data (%d), want (%d:%d:%d)",
+				  queue, ret, chunk, len, max_chunk);
+			goto err;
+		}
+		off += ret;
+		len -= ret;
+	} while (len);
+
+	err = 0;
+err:
+	return err;
+}
+
+static int send_queue(struct libsoccr_sk *sk, int queue, char *buf, __u32 len)
+{
+	logd("\tRestoring TCP %d queue data %u bytes\n", queue, len);
+
+	if (setsockopt(sk->fd, SOL_TCP, TCP_REPAIR_QUEUE, &queue, sizeof(queue)) < 0) {
+		loge("Can't set repair queue");
+		return -1;
+	}
+
+	return __send_queue(sk, queue, buf, len);
+}
+
+int libsoccr_set_queue_bytes(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, unsigned data_size,
+		int queue, char *buf)
+{
+	if (!data || data_size < SOCR_DATA_MIN_SIZE)
+		return -1;
+
+	if (queue == TCP_RECV_QUEUE)
+		return send_queue(sk, TCP_RECV_QUEUE, buf, data->inq_len);
+
+	if (queue == TCP_SEND_QUEUE) {
+		__u32 len, ulen;
+
+		/*
+		 * All data in a write buffer can be divided on two parts sent
+		 * but not yet acknowledged data and unsent data.
+		 * The TCP stack must know which data have been sent, because
+		 * acknowledgment can be received for them. These data must be
+		 * restored in repair mode.
+		 */
+		ulen = data->unsq_len;
+		len = data->outq_len - ulen;
+		if (len && send_queue(sk, TCP_SEND_QUEUE, buf, len))
+			return -2;
+
+		if (ulen) {
+			/*
+			 * The second part of data have never been sent to outside, so
+			 * they can be restored without any tricks.
+			 */
+			tcp_repair_off(sk->fd);
+			if (__send_queue(sk, TCP_SEND_QUEUE, buf + len, ulen))
+				return -3;
+			if (tcp_repair_on(sk->fd))
+				return -4;
+		}
+
+		return 0;
+	}
+
+	return -5;
+}
diff --git a/soccr/soccr.h b/soccr/soccr.h
index 86c43f2..2d8b7f3 100644
--- a/soccr/soccr.h
+++ b/soccr/soccr.h
@@ -56,4 +56,6 @@  char *libsoccr_get_queue_bytes(struct libsoccr_sk *sk, int queue_id, int steal);
 int libsoccr_set_sk_data_unbound(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, unsigned data_size);
 int libsoccr_set_sk_data_noq(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, unsigned data_size);
 int libsoccr_set_sk_data(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, unsigned data_size);
+int libsoccr_set_queue_bytes(struct libsoccr_sk *sk, struct libsoccr_sk_data *data, unsigned data_size,
+		int queue, char *buf);
 #endif