Blob Blame History Raw
From c030e495ed3b5e59acd8c7bdcad8baea1697b909 Mon Sep 17 00:00:00 2001
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Date: Thu, 29 Jul 2021 14:57:17 +0300
Subject: [PATCH 032/245] kerndat: check for set/getsockopt SO_BUF_LOCK
 availability

This is a new kernel feature to let criu restore sockets with kernel
auto-adjusted buffer sizes.

Reviewed-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 criu/cr-check.c        | 10 ++++++++++
 criu/include/kerndat.h |  1 +
 criu/include/sockets.h |  4 ++++
 criu/kerndat.c         | 33 +++++++++++++++++++++++++++++++++
 4 files changed, 48 insertions(+)

diff --git a/criu/cr-check.c b/criu/cr-check.c
index 3575fb3b3..3e268c439 100644
--- a/criu/cr-check.c
+++ b/criu/cr-check.c
@@ -1372,6 +1372,14 @@ static int check_network_lock_nftables(void)
 	return 0;
 }
 
+static int check_sockopt_buf_lock(void)
+{
+	if (!kdat.has_sockopt_buf_lock)
+		return -1;
+
+	return 0;
+}
+
 static int (*chk_feature)(void);
 
 /*
@@ -1490,6 +1498,7 @@ int cr_check(void)
 		ret |= check_ns_pid();
 		ret |= check_apparmor_stacking();
 		ret |= check_network_lock_nftables();
+		ret |= check_sockopt_buf_lock();
 	}
 
 	/*
@@ -1602,6 +1611,7 @@ static struct feature_list feature_list[] = {
 	{ "ns_pid", check_ns_pid },
 	{ "apparmor_stacking", check_apparmor_stacking },
 	{ "network_lock_nftables", check_network_lock_nftables },
+	{ "sockopt_buf_lock", check_sockopt_buf_lock },
 	{ NULL, NULL },
 };
 
diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h
index 80bad7f11..2ded7d1da 100644
--- a/criu/include/kerndat.h
+++ b/criu/include/kerndat.h
@@ -74,6 +74,7 @@ struct kerndat_s {
 	bool has_pidfd_getfd;
 	bool has_nspid;
 	bool has_nftables_concat;
+	bool has_sockopt_buf_lock;
 };
 
 extern struct kerndat_s kdat;
diff --git a/criu/include/sockets.h b/criu/include/sockets.h
index 3e8f3d601..399d38664 100644
--- a/criu/include/sockets.h
+++ b/criu/include/sockets.h
@@ -123,4 +123,8 @@ extern const char *socket_proto_name(unsigned int proto, char *nm, size_t size);
 #define ___socket_family_name(family) __socket_info_helper(socket_family_name, family)
 #define ___socket_proto_name(proto)   __socket_info_helper(socket_proto_name, proto)
 
+#ifndef SO_BUF_LOCK
+#define SO_BUF_LOCK 72
+#endif
+
 #endif /* __CR_SOCKETS_H__ */
diff --git a/criu/kerndat.c b/criu/kerndat.c
index 0e88ba43e..9f6a6ec42 100644
--- a/criu/kerndat.c
+++ b/criu/kerndat.c
@@ -816,6 +816,35 @@ static int kerndat_x86_has_ptrace_fpu_xsave_bug(void)
 	return 0;
 }
 
+int kerndat_sockopt_buf_lock(void)
+{
+	int exit_code = -1;
+	socklen_t len;
+	u32 buf_lock;
+	int sock;
+
+	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sock < 0) {
+		pr_perror("Unable to create a socket");
+		return -1;
+	}
+
+	len = sizeof(buf_lock);
+	if (getsockopt(sock, SOL_SOCKET, SO_BUF_LOCK, &buf_lock, &len)) {
+		if (errno != ENOPROTOOPT) {
+			pr_perror("Unable to get SO_BUF_LOCK with getsockopt");
+			goto err;
+		}
+		kdat.has_sockopt_buf_lock = false;
+	} else
+		kdat.has_sockopt_buf_lock = true;
+
+	exit_code = 0;
+err:
+	close(sock);
+	return exit_code;
+}
+
 #define KERNDAT_CACHE_FILE     KDAT_RUNDIR "/criu.kdat"
 #define KERNDAT_CACHE_FILE_TMP KDAT_RUNDIR "/.criu.kdat"
 
@@ -1359,6 +1388,10 @@ int kerndat_init(void)
 		pr_err("kerndat_has_nftables_concat failed when initializing kerndat.\n");
 		ret = -1;
 	}
+	if (!ret && kerndat_sockopt_buf_lock()) {
+		pr_err("kerndat_sockopt_buf_lock failed when initializing kerndat.\n");
+		ret = -1;
+	}
 
 	kerndat_lsm();
 	kerndat_mmap_min_addr();
-- 
2.35.1