[CRIU] [PATCH 3/3] util-net: Add ability to send/receive arrays of file descriptors

Cyrill Gorcunov gorcunov at openvz.org
Mon Mar 19 11:22:42 EDT 2012


This patch adds ability to send and receive arrays of
file descriptors via SCM messages.

Basically we will need this facility for importing
dumpee fds in further patches.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 include/builtins.h |   22 +++++++
 include/util-net.h |   52 ++++++++++++++++-
 util-net.c         |  162 ++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 192 insertions(+), 44 deletions(-)
 create mode 100644 include/builtins.h

diff --git a/include/builtins.h b/include/builtins.h
new file mode 100644
index 0000000..8e35aa0
--- /dev/null
+++ b/include/builtins.h
@@ -0,0 +1,22 @@
+#ifndef BUILTINS_H__
+#define BUILTINS_H__
+
+#include "compiler.h"
+#include "types.h"
+
+static always_inline void *builtin_memcpy(void *to, const void *from, unsigned int n)
+{
+	int d0, d1, d2;
+	asm volatile("rep ; movsl		\n"
+		     "movl %4,%%ecx		\n"
+		     "andl $3,%%ecx		\n"
+		     "jz 1f			\n"
+		     "rep ; movsb		\n"
+		     "1:"
+		     : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+		     : "0" (n / 4), "g" (n), "1" ((long)to), "2" ((long)from)
+		     : "memory");
+	return to;
+}
+
+#endif /* BUILTINS_H__ */
diff --git a/include/util-net.h b/include/util-net.h
index c5a8c46..5dea3e0 100644
--- a/include/util-net.h
+++ b/include/util-net.h
@@ -1,6 +1,9 @@
 #ifndef UTIL_NET_H_
 #define UTIL_NET_H_
 
+#include <sys/socket.h>
+#include <sys/un.h>
+
 #define UNIX_PATH_MAX (sizeof(struct sockaddr_un) - \
 			(size_t)((struct sockaddr_un *) 0)->sun_path)
 
@@ -8,6 +11,51 @@
 #define SO_PEEK_OFF            42
 #endif
 
-extern int send_fd(int sock, struct sockaddr_un *saddr, int len, int fd);
-extern int recv_fd(int sock);
+/*
+ * Because kernel do kmalloc for user data passed
+ * in SCM messages, and there is SCM_MAX_FD as a limit
+ * for descriptors passed at once -- we're trying to
+ * eliminate pressue on kernel memory manager and use
+ * predefined known to work well size of the message buffer.
+ */
+#define CR_SCM_MSG_SIZE		(1024)
+#define CR_SCM_MAX_FD		(252)
+
+struct scm_fdset {
+	struct msghdr		hdr;
+	struct iovec		iov;
+	char			msg_buf[CR_SCM_MSG_SIZE];
+	int			__pad;
+	union {
+		int		nr_fds_rx;
+		int		nr_fds_tx;
+		int		__nr_fds;
+	};
+};
+
+extern int scm_fdset_update(struct scm_fdset *fdset, int nr_fds);
+extern int *scm_fdset_first(struct scm_fdset *fdset);
+extern int *scm_fdset_init(struct scm_fdset *fdset);
+extern void scm_fdset_set_addr(struct scm_fdset *fdset, struct sockaddr_un *saddr, int saddr_len);
+extern int scm_fdset_send(int sock, struct scm_fdset *fdset);
+extern int scm_fdset_recv(int sock, struct scm_fdset *fdset);
+extern int send_fds(int sock, struct sockaddr_un *saddr, int saddr_len, int *fds, int nr_fds);
+extern int recv_fds(int sock, int *fds, int nr_fds);
+
+static int send_fd(int sock, struct sockaddr_un *saddr, int saddr_len, int fd)
+{
+	return send_fds(sock, saddr, saddr_len, &fd, 1);
+}
+
+static int recv_fd(int sock)
+{
+	int fd, ret;
+
+	ret = recv_fds(sock, &fd, 1);
+	if (ret)
+		return -1;
+
+	return fd;
+}
+
 #endif
diff --git a/util-net.c b/util-net.c
index afd51bd..ea508cb 100644
--- a/util-net.c
+++ b/util-net.c
@@ -1,66 +1,144 @@
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <errno.h>
 
+#include "compiler.h"
+#include "types.h"
+#include "builtins.h"
 #include "syscall.h"
 
-int send_fd(int sock, struct sockaddr_un *saddr, int len, int fd)
+#include "util-net.h"
+
+int scm_fdset_update(struct scm_fdset *fdset, int nr_fds)
+{
+	int min_fd = min(nr_fds, CR_SCM_MAX_FD);
+	struct cmsghdr *cmsg;
+
+	cmsg				= CMSG_FIRSTHDR(&fdset->hdr);
+	fdset->hdr.msg_controllen	= CMSG_LEN(sizeof(int) * min_fd);
+	cmsg->cmsg_len			= fdset->hdr.msg_controllen;
+	fdset->__nr_fds			= min_fd;
+
+	return min_fd;
+}
+
+int *scm_fdset_first(struct scm_fdset *fdset)
 {
-	char cmsgbuf[CMSG_SPACE(sizeof(int))];
-	struct msghdr hdr = { };
-	struct iovec data = { };
-	struct cmsghdr* cmsg;
-	int *cmsg_data;
-	char dummy = '*';
+	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&fdset->hdr);
+	return (int *)CMSG_DATA(cmsg);
+}
 
-	data.iov_base	= &dummy;
-	data.iov_len	= sizeof(dummy);
+int *scm_fdset_init(struct scm_fdset *fdset)
+{
+	struct cmsghdr *cmsg;
 
-	hdr.msg_name	= (struct sockaddr *)saddr;
-	hdr.msg_namelen	= len;
-	hdr.msg_iov	= &data;
-	hdr.msg_iovlen	= 1;
+	BUILD_BUG_ON(CR_SCM_MAX_FD > SCM_MAX_FD);
+	BUILD_BUG_ON(sizeof(fdset->msg_buf) < (CMSG_SPACE(sizeof(int) * CR_SCM_MAX_FD)));
 
-	hdr.msg_control = &cmsgbuf;
-	hdr.msg_controllen = CMSG_LEN(sizeof(int));
+	fdset->__nr_fds			= CR_SCM_MAX_FD;
+	fdset->__pad			= 0;
 
-	cmsg = CMSG_FIRSTHDR(&hdr);
-	cmsg->cmsg_len   = hdr.msg_controllen;
-	cmsg->cmsg_level = SOL_SOCKET;
-	cmsg->cmsg_type  = SCM_RIGHTS;
+	fdset->iov.iov_base		= &fdset->__pad;
+	fdset->iov.iov_len		= sizeof(fdset->__pad);
 
-	cmsg_data = (int *)CMSG_DATA(cmsg);
-	*cmsg_data = fd;
+	/*
+	 * msg_name and msg_namelen should be assigned separately.
+	 */
+	fdset->hdr.msg_iov		= &fdset->iov;
+	fdset->hdr.msg_iovlen		= 1;
 
-	return sys_sendmsg(sock, &hdr, 0);
+	fdset->hdr.msg_control		= &fdset->msg_buf;
+	fdset->hdr.msg_controllen	= CMSG_LEN(sizeof(int) * CR_SCM_MAX_FD);
+
+	cmsg				= CMSG_FIRSTHDR(&fdset->hdr);
+	cmsg->cmsg_len			= fdset->hdr.msg_controllen;
+	cmsg->cmsg_level		= SOL_SOCKET;
+	cmsg->cmsg_type			= SCM_RIGHTS;
+
+	return scm_fdset_first(fdset);
 }
 
-int recv_fd(int sock)
+void scm_fdset_set_addr(struct scm_fdset *fdset, struct sockaddr_un *saddr, int saddr_len)
 {
-	char ccmsg[CMSG_SPACE(sizeof(int))];
-	struct msghdr msg = { };
-	struct iovec iov = { };
-	struct cmsghdr *cmsg;
-	int *cmsg_data;
-	char buf[1];
-	int ret;
+	fdset->hdr.msg_name		= (struct sockaddr *)saddr;
+	fdset->hdr.msg_namelen		= saddr_len;
+}
+
+int scm_fdset_send(int sock, struct scm_fdset *fdset)
+{
+	int ret = sys_sendmsg(sock, &fdset->hdr, 0);
+	if (ret <= 0)
+		return ret;
+
+	return fdset->nr_fds_tx;
+}
 
-	iov.iov_base	= buf;
-	iov.iov_len	= 1;
+int scm_fdset_recv(int sock, struct scm_fdset *fdset)
+{
+	struct cmsghdr *cmsg;
+	int min_fd;
+	int ret = 0;
 
-	msg.msg_iov	= &iov;
-	msg.msg_iovlen	= 1;
-	msg.msg_control	= ccmsg;
-	msg.msg_controllen = sizeof(ccmsg);
+	scm_fdset_update(fdset, CR_SCM_MAX_FD);
 
-	ret = sys_recvmsg(sock, &msg, 0);
+	ret = sys_recvmsg(sock, &fdset->hdr, 0);
 	if (ret < 0)
 		return ret;
 
-	cmsg = CMSG_FIRSTHDR(&msg);
-	if (!cmsg || (cmsg->cmsg_type != SCM_RIGHTS))
-		return -2;
+	cmsg = CMSG_FIRSTHDR(&fdset->hdr);
+	if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
+		return -EINVAL;
+
+	min_fd = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
+	min_fd = min(min_fd, CR_SCM_MAX_FD);
 
-	cmsg_data = (int *)CMSG_DATA(cmsg);
-	return *cmsg_data;
+	fdset->nr_fds_rx = min_fd;
+
+	return 0;
 }
 
+int send_fds(int sock, struct sockaddr_un *saddr, int saddr_len, int *fds, int nr_fds)
+{
+	struct scm_fdset fdset;
+	int *__fds_tx;
+	int i, j, ret;
+
+	scm_fdset_init(&fdset);
+	scm_fdset_set_addr(&fdset, saddr, saddr_len);
+
+	__fds_tx = scm_fdset_first(&fdset);
+
+	for (i = 0; i < nr_fds; i += fdset.nr_fds_tx) {
+		scm_fdset_update(&fdset, nr_fds - i);
+		builtin_memcpy(__fds_tx, &fds[i], sizeof(int) * fdset.nr_fds_tx);
+
+		ret = scm_fdset_send(sock, &fdset);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+int recv_fds(int sock, int *fds, int nr_fds)
+{
+	struct scm_fdset fdset;
+	int *__fds_rx;
+	int i, j, ret;
+
+	scm_fdset_init(&fdset);
+	scm_fdset_set_addr(&fdset, NULL, 0);
+
+	__fds_rx = scm_fdset_first(&fdset);
+
+	for (i = 0; i < nr_fds; i += fdset.nr_fds_rx) {
+		scm_fdset_update(&fdset, CR_SCM_MAX_FD);
+		ret = scm_fdset_recv(sock, &fdset);
+		if (ret < 0)
+			return ret;
+
+		builtin_memcpy(&fds[i], __fds_rx, sizeof(int) * fdset.nr_fds_rx);
+	}
+
+	return 0;
+}
-- 
1.7.7.6



More information about the CRIU mailing list