unix: add support for timerfd syscalls on Linux

timerfd_create, timerfd_gettime and timerfd_settime syscalls have been added.
Fixes golang/go#38733

Change-Id: I306d68103b6efb2515c74f384646210c4b68f66e
Reviewed-on: https://go-review.googlesource.com/c/sys/+/230798
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index 50466d2..d72e7ad 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -46,6 +46,7 @@
 #include <sys/statvfs.h>
 #include <sys/sysinfo.h>
 #include <sys/time.h>
+#include <sys/timerfd.h>
 #include <sys/times.h>
 #include <sys/timex.h>
 #include <sys/un.h>
@@ -410,6 +411,8 @@
 
 type Timex C.struct_timex
 
+type ItimerSpec C.struct_itimerspec
+
 const (
 	TIME_OK    = C.TIME_OK
 	TIME_INS   = C.TIME_INS
diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh
index ab09aaf..780e387 100755
--- a/unix/mkerrors.sh
+++ b/unix/mkerrors.sh
@@ -187,6 +187,7 @@
 #include <sys/select.h>
 #include <sys/signalfd.h>
 #include <sys/socket.h>
+#include <sys/timerfd.h>
 #include <sys/uio.h>
 #include <sys/xattr.h>
 #include <linux/bpf.h>
@@ -480,7 +481,7 @@
 		$2 ~ /^(MS|MNT|UMOUNT)_/ ||
 		$2 ~ /^NS_GET_/ ||
 		$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
-		$2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT)_/ ||
+		$2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT|TFD)_/ ||
 		$2 ~ /^KEXEC_/ ||
 		$2 ~ /^LINUX_REBOOT_CMD_/ ||
 		$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index bbe1abb..4326a81 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -1757,6 +1757,9 @@
 //sys	Syncfs(fd int) (err error)
 //sysnb	Sysinfo(info *Sysinfo_t) (err error)
 //sys	Tee(rfd int, wfd int, len int, flags int) (n int64, err error)
+//sysnb TimerfdCreate(clockid int, flags int) (fd int, err error)
+//sysnb TimerfdGettime(fd int, currValue *ItimerSpec) (err error)
+//sysnb TimerfdSettime(fd int, flags int, newValue *ItimerSpec, oldValue *ItimerSpec) (err error)
 //sysnb	Tgkill(tgid int, tid int, sig syscall.Signal) (err error)
 //sysnb	Times(tms *Tms) (ticks uintptr, err error)
 //sysnb	Umask(mask int) (oldmask int)
@@ -2178,7 +2181,6 @@
 // TimerGetoverrun
 // TimerGettime
 // TimerSettime
-// Timerfd
 // Tkill (obsolete)
 // Tuxcall
 // Umount2
diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go
index e572376..a8221d3 100644
--- a/unix/syscall_linux_test.go
+++ b/unix/syscall_linux_test.go
@@ -19,6 +19,7 @@
 	"strings"
 	"testing"
 	"time"
+	"unsafe"
 
 	"golang.org/x/sys/unix"
 )
@@ -663,3 +664,53 @@
 		t.Fatalf("unexpected return from prctl; got %v, expected %v", v, 1)
 	}
 }
+
+func TestTimerfd(t *testing.T) {
+	var now unix.Timespec
+	if err := unix.ClockGettime(unix.CLOCK_REALTIME, &now); err != nil {
+		t.Fatalf("ClockGettime: %v", err)
+	}
+
+	tfd, err := unix.TimerfdCreate(unix.CLOCK_REALTIME, 0)
+	if err == unix.ENOSYS {
+		t.Skip("timerfd_create system call not implemented")
+	} else if err != nil {
+		t.Fatalf("TimerfdCreate: %v", err)
+	}
+	defer unix.Close(tfd)
+
+	var timeSpec unix.ItimerSpec
+	if err := unix.TimerfdGettime(tfd, &timeSpec); err != nil {
+		t.Fatalf("TimerfdGettime: %v", err)
+	}
+
+	if timeSpec.Value.Nsec != 0 || timeSpec.Value.Sec != 0 {
+		t.Fatalf("TimerfdGettime: timer is already set, but shouldn't be")
+	}
+
+	timeSpec = unix.ItimerSpec{
+		Interval: unix.NsecToTimespec(int64(time.Millisecond)),
+		Value:    now,
+	}
+
+	if err := unix.TimerfdSettime(tfd, unix.TFD_TIMER_ABSTIME, &timeSpec, nil); err != nil {
+		t.Fatalf("TimerfdSettime: %v", err)
+	}
+
+	const totalTicks = 10
+	const bufferLength = 8
+
+	buffer := make([]byte, bufferLength)
+
+	var count uint64 = 0
+	for count < totalTicks {
+		n, err := unix.Read(tfd, buffer)
+		if err != nil {
+			t.Fatalf("Timerfd: %v", err)
+		} else if n != bufferLength {
+			t.Fatalf("Timerfd: got %d bytes from timerfd, expected %d bytes", n, bufferLength)
+		}
+
+		count += *(*uint64)(unsafe.Pointer(&buffer))
+	}
+}
diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go
index 2197394..6e3cfec 100644
--- a/unix/zerrors_linux.go
+++ b/unix/zerrors_linux.go
@@ -2165,6 +2165,8 @@
 	TCP_USER_TIMEOUT                            = 0x12
 	TCP_WINDOW_CLAMP                            = 0xa
 	TCP_ZEROCOPY_RECEIVE                        = 0x23
+	TFD_TIMER_ABSTIME                           = 0x1
+	TFD_TIMER_CANCEL_ON_SET                     = 0x2
 	TIMER_ABSTIME                               = 0x1
 	TIOCM_DTR                                   = 0x2
 	TIOCM_LE                                    = 0x1
diff --git a/unix/zerrors_linux_386.go b/unix/zerrors_linux_386.go
index 028c9d8..5e97411 100644
--- a/unix/zerrors_linux_386.go
+++ b/unix/zerrors_linux_386.go
@@ -342,6 +342,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_amd64.go b/unix/zerrors_linux_amd64.go
index 005970f..47a57fe 100644
--- a/unix/zerrors_linux_amd64.go
+++ b/unix/zerrors_linux_amd64.go
@@ -343,6 +343,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_arm.go b/unix/zerrors_linux_arm.go
index 0541f36..df2eea4 100644
--- a/unix/zerrors_linux_arm.go
+++ b/unix/zerrors_linux_arm.go
@@ -349,6 +349,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_arm64.go b/unix/zerrors_linux_arm64.go
index 9ee8d1b..4e12142 100644
--- a/unix/zerrors_linux_arm64.go
+++ b/unix/zerrors_linux_arm64.go
@@ -336,6 +336,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_mips.go b/unix/zerrors_linux_mips.go
index 4826bd7..a23b080 100644
--- a/unix/zerrors_linux_mips.go
+++ b/unix/zerrors_linux_mips.go
@@ -339,6 +339,8 @@
 	TCSETSW                          = 0x540f
 	TCSETSW2                         = 0x8030542c
 	TCXONC                           = 0x5406
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x80
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x80047478
 	TIOCEXCL                         = 0x740d
diff --git a/unix/zerrors_linux_mips64.go b/unix/zerrors_linux_mips64.go
index 2346dc5..a5a921e 100644
--- a/unix/zerrors_linux_mips64.go
+++ b/unix/zerrors_linux_mips64.go
@@ -339,6 +339,8 @@
 	TCSETSW                          = 0x540f
 	TCSETSW2                         = 0x8030542c
 	TCXONC                           = 0x5406
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x80
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x80047478
 	TIOCEXCL                         = 0x740d
diff --git a/unix/zerrors_linux_mips64le.go b/unix/zerrors_linux_mips64le.go
index e758b61..d088e19 100644
--- a/unix/zerrors_linux_mips64le.go
+++ b/unix/zerrors_linux_mips64le.go
@@ -339,6 +339,8 @@
 	TCSETSW                          = 0x540f
 	TCSETSW2                         = 0x8030542c
 	TCXONC                           = 0x5406
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x80
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x80047478
 	TIOCEXCL                         = 0x740d
diff --git a/unix/zerrors_linux_mipsle.go b/unix/zerrors_linux_mipsle.go
index 2dfe6bb..0ddf9d5 100644
--- a/unix/zerrors_linux_mipsle.go
+++ b/unix/zerrors_linux_mipsle.go
@@ -339,6 +339,8 @@
 	TCSETSW                          = 0x540f
 	TCSETSW2                         = 0x8030542c
 	TCXONC                           = 0x5406
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x80
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x80047478
 	TIOCEXCL                         = 0x740d
diff --git a/unix/zerrors_linux_ppc64.go b/unix/zerrors_linux_ppc64.go
index 5185866..a93ffc1 100644
--- a/unix/zerrors_linux_ppc64.go
+++ b/unix/zerrors_linux_ppc64.go
@@ -393,6 +393,8 @@
 	TCSETSF                          = 0x802c7416
 	TCSETSW                          = 0x802c7415
 	TCXONC                           = 0x2000741e
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_ppc64le.go b/unix/zerrors_linux_ppc64le.go
index 4231b20..c1ea48b 100644
--- a/unix/zerrors_linux_ppc64le.go
+++ b/unix/zerrors_linux_ppc64le.go
@@ -393,6 +393,8 @@
 	TCSETSF                          = 0x802c7416
 	TCSETSW                          = 0x802c7415
 	TCXONC                           = 0x2000741e
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_riscv64.go b/unix/zerrors_linux_riscv64.go
index 6a0b2d2..7def950 100644
--- a/unix/zerrors_linux_riscv64.go
+++ b/unix/zerrors_linux_riscv64.go
@@ -330,6 +330,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_s390x.go b/unix/zerrors_linux_s390x.go
index 95e950f..d39293c 100644
--- a/unix/zerrors_linux_s390x.go
+++ b/unix/zerrors_linux_s390x.go
@@ -403,6 +403,8 @@
 	TCSETXF                          = 0x5434
 	TCSETXW                          = 0x5435
 	TCXONC                           = 0x540a
+	TFD_CLOEXEC                      = 0x80000
+	TFD_NONBLOCK                     = 0x800
 	TIOCCBRK                         = 0x5428
 	TIOCCONS                         = 0x541d
 	TIOCEXCL                         = 0x540c
diff --git a/unix/zerrors_linux_sparc64.go b/unix/zerrors_linux_sparc64.go
index 079762f..3ff3ec6 100644
--- a/unix/zerrors_linux_sparc64.go
+++ b/unix/zerrors_linux_sparc64.go
@@ -392,6 +392,8 @@
 	TCSETSW                          = 0x8024540a
 	TCSETSW2                         = 0x802c540e
 	TCXONC                           = 0x20005406
+	TFD_CLOEXEC                      = 0x400000
+	TFD_NONBLOCK                     = 0x4000
 	TIOCCBRK                         = 0x2000747a
 	TIOCCONS                         = 0x20007424
 	TIOCEXCL                         = 0x2000740d
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index fd2dae8..df21782 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -1450,6 +1450,37 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func TimerfdCreate(clockid int, flags int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_TIMERFD_CREATE, uintptr(clockid), uintptr(flags), 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func TimerfdGettime(fd int, currValue *ItimerSpec) (err error) {
+	_, _, e1 := RawSyscall(SYS_TIMERFD_GETTIME, uintptr(fd), uintptr(unsafe.Pointer(currValue)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func TimerfdSettime(fd int, flags int, newValue *ItimerSpec, oldValue *ItimerSpec) (err error) {
+	_, _, e1 := RawSyscall6(SYS_TIMERFD_SETTIME, uintptr(fd), uintptr(flags), uintptr(unsafe.Pointer(newValue)), uintptr(unsafe.Pointer(oldValue)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
 	_, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
 	if e1 != 0 {
diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go
index af5ab45..416f776 100644
--- a/unix/ztypes_linux.go
+++ b/unix/ztypes_linux.go
@@ -18,6 +18,11 @@
 	_C_long_long int64
 )
 
+type ItimerSpec struct {
+	Interval Timespec
+	Value    Timespec
+}
+
 const (
 	TIME_OK    = 0x0
 	TIME_INS   = 0x1