From ed4f44fbe7402fbb285b0b0da8936cff2e8e46a1 Mon Sep 17 00:00:00 2001
From: dchagin <dchagin@FreeBSD.org>
Date: Sun, 24 May 2015 15:21:25 +0000
Subject: [PATCH] Implement pselect6() system call.

Differential Revision:	https://reviews.freebsd.org/D1051
Reviewed by:	trasz
---
 sys/amd64/linux32/linux32_dummy.c |  1 -
 sys/amd64/linux32/syscalls.master |  4 +-
 sys/compat/linux/linux_misc.c     | 84 +++++++++++++++++++++++++++++++
 sys/i386/linux/linux_dummy.c      |  1 -
 sys/i386/linux/syscalls.master    |  4 +-
 5 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/sys/amd64/linux32/linux32_dummy.c b/sys/amd64/linux32/linux32_dummy.c
index c14711c59104..2461c557819a 100644
--- a/sys/amd64/linux32/linux32_dummy.c
+++ b/sys/amd64/linux32/linux32_dummy.c
@@ -95,7 +95,6 @@ DUMMY(inotify_add_watch);
 DUMMY(inotify_rm_watch);
 /* linux 2.6.16: */
 DUMMY(migrate_pages);
-DUMMY(pselect6);
 DUMMY(ppoll);
 DUMMY(unshare);
 /* linux 2.6.17: */
diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master
index a6e19f00797d..2e936c7a38ee 100644
--- a/sys/amd64/linux32/syscalls.master
+++ b/sys/amd64/linux32/syscalls.master
@@ -508,7 +508,9 @@
 306	AUE_FCHMODAT	STD	{ int linux_fchmodat(l_int dfd, const char *filename, \
 					l_mode_t mode); }
 307	AUE_FACCESSAT	STD	{ int linux_faccessat(l_int dfd, const char *filename, l_int amode, int flag); }
-308	AUE_NULL	STD	{ int linux_pselect6(void); }
+308	AUE_SELECT	STD	{ int linux_pselect6(l_int nfds, l_fd_set *readfds,	\
+					l_fd_set *writefds, l_fd_set *exceptfds,	\
+					struct l_timespec *tsp, l_uintptr_t *sig); }
 309	AUE_NULL	STD	{ int linux_ppoll(void); }
 310	AUE_NULL	STD	{ int linux_unshare(void); }
 ; linux 2.6.17:
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 8a6db39d68db..88fdff55bd33 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -116,6 +116,12 @@ struct l_sysinfo {
 	l_uint		mem_unit;
 	char		_f[20-2*sizeof(l_long)-sizeof(l_int)];	/* padding */
 };
+
+struct l_pselect6arg {
+	l_uintptr_t	ss;
+	l_size_t	ss_len;
+};
+
 int
 linux_sysinfo(struct thread *td, struct linux_sysinfo_args *args)
 {
@@ -2107,6 +2113,84 @@ linux_prlimit64(struct thread *td, struct linux_prlimit64_args *args)
 	return (error);
 }
 
+int
+linux_pselect6(struct thread *td, struct linux_pselect6_args *args)
+{
+	struct timeval utv, tv0, tv1, *tvp;
+	struct l_pselect6arg lpse6;
+	struct l_timespec lts;
+	struct timespec uts;
+	l_sigset_t l_ss;
+	sigset_t *ssp;
+	sigset_t ss;
+	int error;
+
+	ssp = NULL;
+	if (args->sig != NULL) {
+		error = copyin(args->sig, &lpse6, sizeof(lpse6));
+		if (error != 0)
+			return (error);
+		if (lpse6.ss_len != sizeof(l_ss))
+			return (EINVAL);
+		if (lpse6.ss != 0) {
+			error = copyin(PTRIN(lpse6.ss), &l_ss,
+			    sizeof(l_ss));
+			if (error != 0)
+				return (error);
+			linux_to_bsd_sigset(&l_ss, &ss);
+			ssp = &ss;
+		}
+	}
+
+	/*
+	 * Currently glibc changes nanosecond number to microsecond.
+	 * This mean losing precision but for now it is hardly seen.
+	 */
+	if (args->tsp != NULL) {
+		error = copyin(args->tsp, &lts, sizeof(lts));
+		if (error != 0)
+			return (error);
+		uts.tv_sec = lts.tv_sec;
+		uts.tv_nsec = lts.tv_nsec;
+
+		TIMESPEC_TO_TIMEVAL(&utv, &uts);
+		if (itimerfix(&utv))
+			return (EINVAL);
+
+		microtime(&tv0);
+		tvp = &utv;
+	} else
+		tvp = NULL;
+
+	error = kern_pselect(td, args->nfds, args->readfds, args->writefds,
+	    args->exceptfds, tvp, ssp, sizeof(l_int) * 8);
+
+	if (error == 0 && args->tsp != NULL) {
+		if (td->td_retval[0] != 0) {
+			/*
+			 * Compute how much time was left of the timeout,
+			 * by subtracting the current time and the time
+			 * before we started the call, and subtracting
+			 * that result from the user-supplied value.
+			 */
+
+			microtime(&tv1);
+			timevalsub(&tv1, &tv0);
+			timevalsub(&utv, &tv1);
+			if (utv.tv_sec < 0)
+				timevalclear(&utv);
+		} else
+			timevalclear(&utv);
+
+		TIMEVAL_TO_TIMESPEC(&utv, &uts);
+		lts.tv_sec = uts.tv_sec;
+		lts.tv_nsec = uts.tv_nsec;
+		error = copyout(&lts, args->tsp, sizeof(lts));
+	}
+
+	return (error);
+}
+
 int
 linux_sched_rr_get_interval(struct thread *td,
     struct linux_sched_rr_get_interval_args *uap)
diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c
index 7e68f5e7c916..874062b919f9 100644
--- a/sys/i386/linux/linux_dummy.c
+++ b/sys/i386/linux/linux_dummy.c
@@ -91,7 +91,6 @@ DUMMY(inotify_add_watch);
 DUMMY(inotify_rm_watch);
 /* linux 2.6.16: */
 DUMMY(migrate_pages);
-DUMMY(pselect6);
 DUMMY(ppoll);
 DUMMY(unshare);
 /* linux 2.6.17: */
diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master
index a947e5a0c5c9..4455c33ea7c5 100644
--- a/sys/i386/linux/syscalls.master
+++ b/sys/i386/linux/syscalls.master
@@ -516,7 +516,9 @@
 306	AUE_FCHMODAT	STD	{ int linux_fchmodat(l_int dfd, const char *filename, \
 					l_mode_t mode); }
 307	AUE_FACCESSAT	STD	{ int linux_faccessat(l_int dfd, const char *filename, l_int amode, l_int flag); }
-308	AUE_NULL	STD	{ int linux_pselect6(void); }
+308	AUE_SELECT	STD	{ int linux_pselect6(l_int nfds, l_fd_set *readfds,	\
+					l_fd_set *writefds, l_fd_set *exceptfds,	\
+					struct l_timespec *tsp, l_uintptr_t *sig); }
 309	AUE_NULL	STD	{ int linux_ppoll(void); }
 310	AUE_NULL	STD	{ int linux_unshare(void); }
 ; linux 2.6.17: