summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Cantrill <bryan@joyent.com>2015-09-04 08:32:01 -0700
committerRobert Mustacchi <rm@joyent.com>2015-10-16 15:07:22 -0700
commit6a72db4a7fa12c3e0d1c1cf91a07390739fa0fbf (patch)
tree01b80bd94b70941b4bebdf46773bfff1bdbdab3e
parentf3bb54f387fc03cf651e19bbee54cc88ee51bb29 (diff)
downloadillumos-joyent-6a72db4a7fa12c3e0d1c1cf91a07390739fa0fbf.tar.gz
6208 add support for timerfd
Reviewed by: Gordon Ross <gwr@nexenta.com> Approved by: Dan McDonald <danmcd@omniti.com>
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c5
-rw-r--r--usr/src/lib/libc/amd64/Makefile1
-rw-r--r--usr/src/lib/libc/i386/Makefile.com1
-rw-r--r--usr/src/lib/libc/port/mapfile-vers7
-rw-r--r--usr/src/lib/libc/port/sys/timerfd.c93
-rw-r--r--usr/src/lib/libc/sparc/Makefile.com1
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile.com1
-rw-r--r--usr/src/man/man3c/Makefile6
-rw-r--r--usr/src/man/man3c/timerfd_create.3c201
-rw-r--r--usr/src/man/man5/Makefile2
-rw-r--r--usr/src/man/man5/timerfd.547
-rw-r--r--usr/src/pkg/manifests/SUNWcs.mf4
-rw-r--r--usr/src/pkg/manifests/system-header.mf1
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc3
-rw-r--r--usr/src/pkg/manifests/system-library.man5.inc1
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/io/timerfd.c586
-rw-r--r--usr/src/uts/common/io/timerfd.conf16
-rw-r--r--usr/src/uts/common/os/clock_highres.c7
-rw-r--r--usr/src/uts/common/os/clock_realtime.c16
-rw-r--r--usr/src/uts/common/os/timer.c13
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/timer.h31
-rw-r--r--usr/src/uts/common/sys/timerfd.h81
-rw-r--r--usr/src/uts/intel/Makefile.intel2
-rw-r--r--usr/src/uts/intel/timerfd/Makefile68
-rw-r--r--usr/src/uts/sparc/Makefile.sparc1
-rw-r--r--usr/src/uts/sparc/timerfd/Makefile68
28 files changed, 1246 insertions, 20 deletions
diff --git a/usr/src/cmd/devfsadm/misc_link.c b/usr/src/cmd/devfsadm/misc_link.c
index abb133bc6d..bf59fb5e6b 100644
--- a/usr/src/cmd/devfsadm/misc_link.c
+++ b/usr/src/cmd/devfsadm/misc_link.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <regex.h>
@@ -126,6 +126,9 @@ static devfsadm_create_t misc_cbt[] = {
"(^kdmouse$)|(^rootprop$)",
TYPE_EXACT | DRV_RE, ILEVEL_0, node_name
},
+ { "pseudo", "ddi_pseudo", "timerfd",
+ TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
+ },
{ "pseudo", "ddi_pseudo", "tod",
TYPE_EXACT | DRV_EXACT, ILEVEL_0, node_name
},
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index b5e54b19fa..dbda6c0c31 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -909,6 +909,7 @@ PORTSYS= \
tasksys.o \
time.o \
time_util.o \
+ timerfd.o \
ucontext.o \
unlink.o \
ustat.o \
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index d7e77502f2..25ba0a2743 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -949,6 +949,7 @@ PORTSYS= \
tasksys.o \
time.o \
time_util.o \
+ timerfd.o \
ucontext.o \
unlink.o \
ustat.o \
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 017c7c31bc..a0e21e250f 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -93,6 +93,13 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.16 { # timerfd
+ protected:
+ timerfd_create;
+ timerfd_gettime;
+ timerfd_settime;
+} ILLUMOS_0.15;
+
SYMBOL_VERSION ILLUMOS_0.15 { # epoll(3C)
protected:
epoll_create;
diff --git a/usr/src/lib/libc/port/sys/timerfd.c b/usr/src/lib/libc/port/sys/timerfd.c
new file mode 100644
index 0000000000..cb2e17adf7
--- /dev/null
+++ b/usr/src/lib/libc/port/sys/timerfd.c
@@ -0,0 +1,93 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/timerfd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+int
+timerfd_create(int clockid, int flags)
+{
+ int oflags = O_RDWR;
+ int fd;
+
+ if (flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (flags & TFD_NONBLOCK)
+ oflags |= O_NONBLOCK;
+
+ if (flags & TFD_CLOEXEC)
+ oflags |= O_CLOEXEC;
+
+ if ((fd = open("/dev/timerfd", oflags)) < 0)
+ return (-1);
+
+ if (ioctl(fd, TIMERFDIOC_CREATE, clockid) != 0) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+int
+timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
+ struct itimerspec *old_value)
+{
+ timerfd_settime_t st;
+ int rval;
+
+ if (flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ st.tfd_settime_flags = flags;
+ st.tfd_settime_value = (uint64_t)(uintptr_t)new_value;
+ st.tfd_settime_ovalue = (uint64_t)(uintptr_t)old_value;
+
+ rval = ioctl(fd, TIMERFDIOC_SETTIME, &st);
+
+ if (rval == -1 && errno == ENOTTY) {
+ /*
+ * Linux has us return EINVAL when the file descriptor is valid
+ * but is not a timerfd file descriptor -- and LTP explicitly
+ * checks this case.
+ */
+ errno = EINVAL;
+ }
+
+ return (rval);
+}
+
+int
+timerfd_gettime(int fd, struct itimerspec *curr_value)
+{
+ int rval = ioctl(fd, TIMERFDIOC_GETTIME, curr_value);
+
+ if (rval == -1 && errno == ENOTTY) {
+ /*
+ * See comment in timerfd_settime(), above.
+ */
+ errno = EINVAL;
+ }
+
+ return (rval);
+}
diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com
index dc965fe6ac..94036d831e 100644
--- a/usr/src/lib/libc/sparc/Makefile.com
+++ b/usr/src/lib/libc/sparc/Makefile.com
@@ -983,6 +983,7 @@ PORTSYS= \
tasksys.o \
time.o \
time_util.o \
+ timerfd.o \
ucontext.o \
unlink.o \
ustat.o \
diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com
index 415aaf2be2..2156bad20b 100644
--- a/usr/src/lib/libc/sparcv9/Makefile.com
+++ b/usr/src/lib/libc/sparcv9/Makefile.com
@@ -927,6 +927,7 @@ PORTSYS= \
tasksys.o \
time.o \
time_util.o \
+ timerfd.o \
ucontext.o \
unlink.o \
ustat.o \
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index f6cadebe95..c38d65a57a 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -473,6 +473,7 @@ MANFILES= __fbufsize.3c \
timer_delete.3c \
timer_settime.3c \
timeradd.3c \
+ timerfd_create.3c \
tmpfile.3c \
tmpnam.3c \
toascii.3c \
@@ -1211,6 +1212,8 @@ MANLINKS= FD_CLR.3c \
timer_gettime.3c \
timerclear.3c \
timercmp.3c \
+ timerfd_gettime.3c \
+ timerfd_settime.3c \
timerisset.3c \
timersub.3c \
tmpnam_r.3c \
@@ -2250,6 +2253,9 @@ timercmp.3c := LINKSRC = timeradd.3c
timerisset.3c := LINKSRC = timeradd.3c
timersub.3c := LINKSRC = timeradd.3c
+timerfd_gettime.3c := LINKSRC = timerfd_create.3c
+timerfd_settime.3c := LINKSRC = timerfd_create.3c
+
tempnam.3c := LINKSRC = tmpnam.3c
tmpnam_r.3c := LINKSRC = tmpnam.3c
diff --git a/usr/src/man/man3c/timerfd_create.3c b/usr/src/man/man3c/timerfd_create.3c
new file mode 100644
index 0000000000..84df47e245
--- /dev/null
+++ b/usr/src/man/man3c/timerfd_create.3c
@@ -0,0 +1,201 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc. All Rights Reserved.
+.\"
+.Dd Feb 23, 2105
+.Dt TIMERFD 3C
+.Os
+.Sh NAME
+.Nm timerfd_create ,
+.Nm timerfd_settime ,
+.Nm timerfd_gettime
+.Nd create and manipulate timers via a file descriptor interface
+.Sh SYNOPSIS
+.In sys/timerfd.h
+.Ft int
+.Fo timerfd_create
+.Fa "int clockid"
+.Fa "int flags"
+.Fc
+.Ft int
+.Fo timerfd_settime
+.Fa "int fd"
+.Fa "int flags"
+.Fa "const struct itimerspec *restrict value"
+.Fa "struct itimterspec *restrict ovalue"
+.Fc
+.Ft int
+.Fo timerfd_gettime
+.Fa "int fd"
+.Fa "struct itimerspec *value"
+.Fc
+.Sh DESCRIPTION
+These routines create and manipulate timers in which events are associated
+with a file descriptor, allowing for timer-based events to be used
+in file-descriptor based facilities like
+.Xr poll 2 ,
+.Xr port_get 3C
+or
+.Xr epoll_wait 3C .
+The
+.Fn timerfd_create
+function creates a timer with the clock
+type specified by
+.Fa clockid .
+The
+.Sy CLOCK_REALTIME
+and
+.Sy CLOCK_HIGHRES
+clock types, as defined in
+.Xr timer_create 3C ,
+are supported by
+.Fn timerfd_create .
+(Note that
+.Sy CLOCK_MONOTONIC
+may be used as an alias for
+.Sy CLOCK_HIGHRES Ns .)
+.Pp
+The
+.Fa flags
+argument specifies additional parameters for the timer instance, and can have
+any of the following values:
+.Bl -hang -width Ds
+.It Sy TFD_CLOEXEC
+.Bd -filled -compact
+Instance will be closed upon an
+.Xr exec 2 ;
+see
+.Xr open 2 Ns 's
+description of
+.Sy O_CLOEXEC .
+.Ed
+.It Sy TFD_NONBLOCK
+.Bd -filled -compact
+Instance will be set to be non-blocking. A
+.Xr read 2
+on a
+.Sy timerfd
+instance that has been initialized with
+.Sy TFD_NONBLOCK
+will return
+.Sy EAGAIN
+in lieu of blocking if the
+timer has not expired since the last
+.Fn timerfd_settime
+or successful
+.Fn read .
+.Ed
+.El
+.Pp
+The following operations can be performed upon a
+.Sy timerfd
+instance:
+.Bl -hang -width Ds
+.It Sy read(2)
+.Bd -filled -compact
+Atomically reads and clears the number of timer expirations since the
+last successful
+.Xr read 2
+or
+.Fn timerfd_settime .
+Upon success,
+the number of expirations will be copied into the eight byte buffer
+passed to the system call. If there have been no expirations of the
+timer since the last successful
+.Xr read 2
+or
+.Fn timerfd_sttime ,
+.Xr read 2
+will block until at least the next expiration,
+or return
+.Sy EAGAIN
+if the instance was created with
+.Sy TFD_NONBLOCK .
+Note that if multiple threads are blocked in
+.Xr read 2
+for the same timer, only one of them will return upon
+a single timer expiration.
+.Pp
+If the buffer specified to
+.Xr read 2
+is less than
+eight bytes in length,
+.Sy EINAVL
+will be returned.
+.Ed
+.It Sy poll(2), port_get(3C), epoll_wait(3C)
+.Bd -filled -compact
+Provide notification when the timer expires or has expired in the past without
+a more recent
+.Xr read 2 .
+Note that threads being simultaneously
+blocked in
+.Xr read 2
+and
+.Xr poll 2
+(or equivalents) for the same
+timer constitute an application-level race; on a timer expiration,
+the thread blocked in
+.Xr poll 2
+may or may not return depending on how
+it is scheduled with respect to the thread blocked in
+.Xr read 2 .
+.Ed
+.It Sy timerfd_gettime()
+.Bd -filled -compact
+Returns the amount of time until the next timer expiration, with the
+same functional signature and semantics as
+.Xr timer_gettime 3C .
+.Ed
+.It Sy timerfd_settime()
+.Bd -filled -compact
+Sets or disarms the timer, with the
+same functional signature and semantics as
+.Xr timer_settime 3C .
+.Ed
+.El
+.Sh RETURN VALUES
+Upon succesful completion, a file descriptor associated with the instance
+is returned. Otherwise,
+.Sy -1
+is returned and errno is set to indicate the error.
+.Sh ERRORS
+The
+.Fn timerfd_create()
+function will fail if:
+.Bl -tag -width Er
+.It Er EINAL
+The
+.Fa flags
+are invalid.
+.It Er EMFILE
+There are currently
+.Pf { Sy OPEN_MAX Ns }
+file descriptors open in the calling process.
+.It Er EPERM
+The
+.Fa clock_id ,
+is
+.Sy CLOCK_HIGHRES
+and the
+.Pf { Sy PRIV_PROC_CLOCK_HIGHRES Ns }
+is not asserted in the effective set of the calling process.
+.El
+.Sh SEE ALSO
+.Xr poll 2 ,
+.Xr port_get 3C ,
+.Xr epoll_wait 3C ,
+.Xr timer_create 3C ,
+.Xr timer_gettime 3C ,
+.Xr timer_settime 3C ,
+.Xr privileages 5 ,
+.Xr timerfd 5
diff --git a/usr/src/man/man5/Makefile b/usr/src/man/man5/Makefile
index 4784603013..c30af8e1d1 100644
--- a/usr/src/man/man5/Makefile
+++ b/usr/src/man/man5/Makefile
@@ -14,6 +14,7 @@
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2014 Nexenta Systems, Inc.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
#
include $(SRC)/Makefile.master
@@ -121,6 +122,7 @@ MANFILES= Intro.5 \
tecla.5 \
term.5 \
threads.5 \
+ timerfd.5 \
trusted_extensions.5 \
vgrindefs.5 \
zones.5 \
diff --git a/usr/src/man/man5/timerfd.5 b/usr/src/man/man5/timerfd.5
new file mode 100644
index 0000000000..3229095b49
--- /dev/null
+++ b/usr/src/man/man5/timerfd.5
@@ -0,0 +1,47 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc. All Rights Reserved.
+.\"
+.Dd Feb 23, 2015
+.Dt TIMERFD 5
+.Os
+.Sh NAME
+.Nm timerfd
+.Nd Linux-compatible timer notification facility
+.Sh SYNOPSIS
+.In sys/timerfd.h
+.Sh DESCRIPTION
+.Nm
+is a Linux-borne facility for creating POSIX timers and
+receiving their subsequent events via a file descriptor.
+The facility itself is arguably unnecessary:
+portable code can either use the timeout value present in
+.Xr poll 2 /
+.Xr port_get 3C
+or -- if this is deemed of unacceptably poor resolution -- create a POSIX timer
+via
+.Xr timer_create 3C
+and use the resulting signal to induce an
+.Sy EINTR
+to polling threads. (For code that need not be
+portable, the
+.Sy SIGEV_PORT
+signal notification allows for explicit, event-oriented timer notification to be
+sent to a specified port; see
+.Xr signal.h 3HEAD
+for details.) This facility therefore exists only to accommodate Linux-borne
+applications and binaries; it is compatible with its Linux antecedent in both
+binary interface and in semantics.
+.Sh SEE ALSO
+.Xr timerfd_create 3C ,
+.Xr timerfd_gettime 3C ,
+.Xr timerfd_settime 3C
diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf
index 85285ae7cd..47923f1f78 100644
--- a/usr/src/pkg/manifests/SUNWcs.mf
+++ b/usr/src/pkg/manifests/SUNWcs.mf
@@ -323,6 +323,7 @@ driver name=logindmux
driver name=ptm clone_perms="ptmx 0666 root sys"
driver name=pts perms="* 0644 root sys" perms="0 0620 root tty" \
perms="1 0620 root tty" perms="2 0620 root tty" perms="3 0620 root tty"
+driver name=timerfd perms="* 0666 root sys"
file path=etc/.login group=sys preserve=renamenew
file path=etc/cron.d/.proto group=sys mode=0744
file path=etc/cron.d/at.deny group=sys preserve=true
@@ -868,6 +869,7 @@ file path=usr/kernel/drv/$(ARCH64)/ksyms group=sys
file path=usr/kernel/drv/$(ARCH64)/logindmux group=sys
file path=usr/kernel/drv/$(ARCH64)/ptm group=sys
file path=usr/kernel/drv/$(ARCH64)/pts group=sys
+file path=usr/kernel/drv/$(ARCH64)/timerfd group=sys
$(i386_ONLY)file path=usr/kernel/drv/dump group=sys
file path=usr/kernel/drv/dump.conf group=sys
$(i386_ONLY)file path=usr/kernel/drv/eventfd group=sys
@@ -884,6 +886,8 @@ $(i386_ONLY)file path=usr/kernel/drv/ptm group=sys
file path=usr/kernel/drv/ptm.conf group=sys
$(i386_ONLY)file path=usr/kernel/drv/pts group=sys
file path=usr/kernel/drv/pts.conf group=sys
+$(i386_ONLY)file path=usr/kernel/drv/timerfd group=sys
+file path=usr/kernel/drv/timerfd.conf group=sys
file path=usr/kernel/exec/$(ARCH64)/javaexec group=sys mode=0755
file path=usr/kernel/exec/$(ARCH64)/shbinexec group=sys mode=0755
$(i386_ONLY)file path=usr/kernel/exec/javaexec group=sys mode=0755
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index b72d713cd8..08f0b19416 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -1516,6 +1516,7 @@ file path=usr/include/sys/time_impl.h
file path=usr/include/sys/time_std_impl.h
file path=usr/include/sys/timeb.h
file path=usr/include/sys/timer.h
+file path=usr/include/sys/timerfd.h
file path=usr/include/sys/times.h
file path=usr/include/sys/timex.h
file path=usr/include/sys/timod.h
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index 27268505b3..ae061edac9 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -468,6 +468,7 @@ file path=usr/share/man/man3c/timer_create.3c
file path=usr/share/man/man3c/timer_delete.3c
file path=usr/share/man/man3c/timer_settime.3c
file path=usr/share/man/man3c/timeradd.3c
+file path=usr/share/man/man3c/timerfd_create.3c
file path=usr/share/man/man3c/tmpfile.3c
file path=usr/share/man/man3c/tmpnam.3c
file path=usr/share/man/man3c/toascii.3c
@@ -1281,6 +1282,8 @@ link path=usr/share/man/man3c/timer_getoverrun.3c target=timer_settime.3c
link path=usr/share/man/man3c/timer_gettime.3c target=timer_settime.3c
link path=usr/share/man/man3c/timerclear.3c target=timeradd.3c
link path=usr/share/man/man3c/timercmp.3c target=timeradd.3c
+link path=usr/share/man/man3c/timerfd_gettime.3c target=timerfd_create.3c
+link path=usr/share/man/man3c/timerfd_settime.3c target=timerfd_create.3c
link path=usr/share/man/man3c/timerisset.3c target=timeradd.3c
link path=usr/share/man/man3c/timersub.3c target=timeradd.3c
link path=usr/share/man/man3c/tmpnam_r.3c target=tmpnam.3c
diff --git a/usr/src/pkg/manifests/system-library.man5.inc b/usr/src/pkg/manifests/system-library.man5.inc
index fd222bcec4..63d883e984 100644
--- a/usr/src/pkg/manifests/system-library.man5.inc
+++ b/usr/src/pkg/manifests/system-library.man5.inc
@@ -61,6 +61,7 @@ file path=usr/share/man/man5/pkcs11_softtoken.5
file path=usr/share/man/man5/pkcs11_tpm.5
file path=usr/share/man/man5/regex.5
file path=usr/share/man/man5/regexp.5
+file path=usr/share/man/man5/timerfd.5
file path=usr/share/man/man5/threads.5
link path=usr/share/man/man5/advance.5 target=regexp.5
link path=usr/share/man/man5/compile.5 target=regexp.5
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 3421b83719..cd9da11ac8 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1032,6 +1032,8 @@ GEN_DRV_OBJS += gen_drv.o
TCLIENT_OBJS += tclient.o
+TIMERFD_OBJS += timerfd.o
+
TPHCI_OBJS += tphci.o
TVHCI_OBJS += tvhci.o
diff --git a/usr/src/uts/common/io/timerfd.c b/usr/src/uts/common/io/timerfd.c
new file mode 100644
index 0000000000..29eea9b24a
--- /dev/null
+++ b/usr/src/uts/common/io/timerfd.c
@@ -0,0 +1,586 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Support for the timerfd facility, a Linux-borne facility that allows
+ * POSIX.1b timers to be created and manipulated via a file descriptor
+ * interface.
+ */
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/timerfd.h>
+#include <sys/conf.h>
+#include <sys/vmem.h>
+#include <sys/sysmacros.h>
+#include <sys/filio.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/timer.h>
+
+struct timerfd_state;
+typedef struct timerfd_state timerfd_state_t;
+
+struct timerfd_state {
+ kmutex_t tfd_lock; /* lock protecting state */
+ kcondvar_t tfd_cv; /* condvar */
+ pollhead_t tfd_pollhd; /* poll head */
+ uint64_t tfd_fired; /* # of times fired */
+ itimer_t tfd_itimer; /* underlying itimer */
+ timerfd_state_t *tfd_next; /* next state on global list */
+};
+
+/*
+ * Internal global variables.
+ */
+static kmutex_t timerfd_lock; /* lock protecting state */
+static dev_info_t *timerfd_devi; /* device info */
+static vmem_t *timerfd_minor; /* minor number arena */
+static void *timerfd_softstate; /* softstate pointer */
+static timerfd_state_t *timerfd_state; /* global list of state */
+
+static itimer_t *
+timerfd_itimer_lock(timerfd_state_t *state)
+{
+ itimer_t *it = &state->tfd_itimer;
+
+ mutex_enter(&state->tfd_lock);
+
+ while (it->it_lock & ITLK_LOCKED) {
+ it->it_blockers++;
+ cv_wait(&it->it_cv, &state->tfd_lock);
+ it->it_blockers--;
+ }
+
+ it->it_lock |= ITLK_LOCKED;
+
+ mutex_exit(&state->tfd_lock);
+
+ return (it);
+}
+
+static void
+timerfd_itimer_unlock(timerfd_state_t *state, itimer_t *it)
+{
+ VERIFY(it == &state->tfd_itimer);
+ VERIFY(it->it_lock & ITLK_LOCKED);
+
+ mutex_enter(&state->tfd_lock);
+
+ it->it_lock &= ~ITLK_LOCKED;
+
+ if (it->it_blockers)
+ cv_signal(&it->it_cv);
+
+ mutex_exit(&state->tfd_lock);
+}
+
+static void
+timerfd_fire(itimer_t *it)
+{
+ timerfd_state_t *state = it->it_frontend;
+ uint64_t oval;
+
+ mutex_enter(&state->tfd_lock);
+ oval = state->tfd_fired++;
+ mutex_exit(&state->tfd_lock);
+
+ if (oval == 0) {
+ cv_broadcast(&state->tfd_cv);
+ pollwakeup(&state->tfd_pollhd, POLLRDNORM | POLLIN);
+ }
+}
+
+/*ARGSUSED*/
+static int
+timerfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ timerfd_state_t *state;
+ major_t major = getemajor(*devp);
+ minor_t minor = getminor(*devp);
+
+ if (minor != TIMERFDMNRN_TIMERFD)
+ return (ENXIO);
+
+ mutex_enter(&timerfd_lock);
+
+ minor = (minor_t)(uintptr_t)vmem_alloc(timerfd_minor, 1,
+ VM_BESTFIT | VM_SLEEP);
+
+ if (ddi_soft_state_zalloc(timerfd_softstate, minor) != DDI_SUCCESS) {
+ vmem_free(timerfd_minor, (void *)(uintptr_t)minor, 1);
+ mutex_exit(&timerfd_lock);
+ return (NULL);
+ }
+
+ state = ddi_get_soft_state(timerfd_softstate, minor);
+ *devp = makedevice(major, minor);
+
+ state->tfd_next = timerfd_state;
+ timerfd_state = state;
+
+ mutex_exit(&timerfd_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_read(dev_t dev, uio_t *uio, cred_t *cr)
+{
+ timerfd_state_t *state;
+ minor_t minor = getminor(dev);
+ uint64_t val;
+ int err;
+
+ if (uio->uio_resid < sizeof (val))
+ return (EINVAL);
+
+ state = ddi_get_soft_state(timerfd_softstate, minor);
+
+ mutex_enter(&state->tfd_lock);
+
+ while (state->tfd_fired == 0) {
+ if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
+ mutex_exit(&state->tfd_lock);
+ return (EAGAIN);
+ }
+
+ if (!cv_wait_sig_swap(&state->tfd_cv, &state->tfd_lock)) {
+ mutex_exit(&state->tfd_lock);
+ return (EINTR);
+ }
+ }
+
+ /*
+ * Our tfd_fired is non-zero; slurp its value and then clear it.
+ */
+ val = state->tfd_fired;
+ state->tfd_fired = 0;
+ mutex_exit(&state->tfd_lock);
+
+ err = uiomove(&val, sizeof (val), UIO_READ, uio);
+
+ return (err);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ timerfd_state_t *state;
+ minor_t minor = getminor(dev);
+ short revents = 0;
+
+ state = ddi_get_soft_state(timerfd_softstate, minor);
+
+ mutex_enter(&state->tfd_lock);
+
+ if (state->tfd_fired > 0)
+ revents |= POLLRDNORM | POLLIN;
+
+ if (!(*reventsp = revents & events) && !anyyet)
+ *phpp = &state->tfd_pollhd;
+
+ mutex_exit(&state->tfd_lock);
+
+ return (0);
+}
+
+static int
+timerfd_copyin(uintptr_t addr, itimerspec_t *dest)
+{
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin((void *)addr, dest, sizeof (itimerspec_t)) != 0)
+ return (EFAULT);
+ } else {
+ itimerspec32_t dest32;
+
+ if (copyin((void *)addr, &dest32, sizeof (itimerspec32_t)) != 0)
+ return (EFAULT);
+
+ ITIMERSPEC32_TO_ITIMERSPEC(dest, &dest32);
+ }
+
+ if (itimerspecfix(&dest->it_value) ||
+ (itimerspecfix(&dest->it_interval) &&
+ timerspecisset(&dest->it_value))) {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+timerfd_copyout(itimerspec_t *src, uintptr_t addr)
+{
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(src, (void *)addr, sizeof (itimerspec_t)) != 0)
+ return (EFAULT);
+ } else {
+ itimerspec32_t src32;
+
+ if (ITIMERSPEC_OVERFLOW(src))
+ return (EOVERFLOW);
+
+ ITIMERSPEC_TO_ITIMERSPEC32(&src32, src);
+
+ if (copyout(&src32, (void *)addr, sizeof (itimerspec32_t)) != 0)
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ itimerspec_t when, oval;
+ timerfd_state_t *state;
+ minor_t minor = getminor(dev);
+ int err;
+ itimer_t *it;
+
+ state = ddi_get_soft_state(timerfd_softstate, minor);
+
+ switch (cmd) {
+ case TIMERFDIOC_CREATE: {
+ if (arg == TIMERFD_MONOTONIC)
+ arg = CLOCK_MONOTONIC;
+
+ it = timerfd_itimer_lock(state);
+
+ if (it->it_backend != NULL) {
+ timerfd_itimer_unlock(state, it);
+ return (EEXIST);
+ }
+
+ if ((it->it_backend = clock_get_backend(arg)) == NULL) {
+ timerfd_itimer_unlock(state, it);
+ return (EINVAL);
+ }
+
+ /*
+ * We need to provide a proc structure only for purposes
+ * of locking CLOCK_REALTIME-based timers -- it is safe to
+ * provide p0 here.
+ */
+ it->it_proc = &p0;
+
+ err = it->it_backend->clk_timer_create(it, timerfd_fire);
+
+ if (err != 0) {
+ it->it_backend = NULL;
+ timerfd_itimer_unlock(state, it);
+ return (err);
+ }
+
+ it->it_frontend = state;
+ timerfd_itimer_unlock(state, it);
+
+ return (0);
+ }
+
+ case TIMERFDIOC_GETTIME: {
+ it = timerfd_itimer_lock(state);
+
+ if (it->it_backend == NULL) {
+ timerfd_itimer_unlock(state, it);
+ return (ENODEV);
+ }
+
+ err = it->it_backend->clk_timer_gettime(it, &when);
+ timerfd_itimer_unlock(state, it);
+
+ if (err != 0)
+ return (err);
+
+ if ((err = timerfd_copyout(&when, arg)) != 0)
+ return (err);
+
+ return (0);
+ }
+
+ case TIMERFDIOC_SETTIME: {
+ timerfd_settime_t st;
+
+ if (copyin((void *)arg, &st, sizeof (st)) != 0)
+ return (EFAULT);
+
+ if ((err = timerfd_copyin(st.tfd_settime_value, &when)) != 0)
+ return (err);
+
+ it = timerfd_itimer_lock(state);
+
+ if (it->it_backend == NULL) {
+ timerfd_itimer_unlock(state, it);
+ return (ENODEV);
+ }
+
+ if (st.tfd_settime_ovalue != NULL) {
+ err = it->it_backend->clk_timer_gettime(it, &oval);
+
+ if (err != 0) {
+ timerfd_itimer_unlock(state, it);
+ return (err);
+ }
+ }
+
+ /*
+ * Before we set the time, we're going to clear tfd_fired.
+ * This can potentially race with the (old) timer firing, but
+ * the window is deceptively difficult to close: if we were
+ * to simply clear tfd_fired after the call to the backend
+ * returned, we would run the risk of plowing a firing of the
+ * new timer. Ultimately, the race can only be resolved by
+ * the backend, which would likely need to be extended with a
+ * function to call back into when the timer is between states
+ * (that is, after the timer can no longer fire with the old
+ * timer value, but before it can fire with the new one).
+ * This is straightforward enough for backends that set a
+ * timer's value by deleting the old one and adding the new
+ * one, but for those that modify the timer value in place
+ * (e.g., cyclics), the required serialization is necessarily
+ * delicate: the function would have to be callable from
+ * arbitrary interrupt context. While implementing all of
+ * this is possible, it does not (for the moment) seem worth
+ * it: if the timer is firing at essentially the same moment
+ * that it's being reprogrammed, there is a higher-level race
+ * with respect to timerfd usage that the progam itself will
+ * have to properly resolve -- and it seems reasonable to
+ * simply allow the program to resolve it in this case.
+ */
+ mutex_enter(&state->tfd_lock);
+ state->tfd_fired = 0;
+ mutex_exit(&state->tfd_lock);
+
+ err = it->it_backend->clk_timer_settime(it,
+ st.tfd_settime_flags & TFD_TIMER_ABSTIME ?
+ TIMER_ABSTIME : TIMER_RELTIME, &when);
+ timerfd_itimer_unlock(state, it);
+
+ if (err != 0 || st.tfd_settime_ovalue == NULL)
+ return (err);
+
+ if ((err = timerfd_copyout(&oval, st.tfd_settime_ovalue)) != 0)
+ return (err);
+
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ timerfd_state_t *state, **sp;
+ itimer_t *it;
+ minor_t minor = getminor(dev);
+
+ state = ddi_get_soft_state(timerfd_softstate, minor);
+
+ if (state->tfd_pollhd.ph_list != NULL) {
+ pollwakeup(&state->tfd_pollhd, POLLERR);
+ pollhead_clean(&state->tfd_pollhd);
+ }
+
+ /*
+ * No one can get to this timer; we don't need to lock it -- we can
+ * just call on the backend to delete it.
+ */
+ it = &state->tfd_itimer;
+
+ if (it->it_backend != NULL)
+ it->it_backend->clk_timer_delete(it);
+
+ mutex_enter(&timerfd_lock);
+
+ /*
+ * Remove our state from our global list.
+ */
+ for (sp = &timerfd_state; *sp != state; sp = &((*sp)->tfd_next))
+ VERIFY(*sp != NULL);
+
+ *sp = (*sp)->tfd_next;
+
+ ddi_soft_state_free(timerfd_softstate, minor);
+ vmem_free(timerfd_minor, (void *)(uintptr_t)minor, 1);
+
+ mutex_exit(&timerfd_lock);
+
+ return (0);
+}
+
+static int
+timerfd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&timerfd_lock);
+
+ if (ddi_soft_state_init(&timerfd_softstate,
+ sizeof (timerfd_state_t), 0) != 0) {
+ cmn_err(CE_NOTE, "/dev/timerfd failed to create soft state");
+ mutex_exit(&timerfd_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "timerfd", S_IFCHR,
+ TIMERFDMNRN_TIMERFD, DDI_PSEUDO, NULL) == DDI_FAILURE) {
+ cmn_err(CE_NOTE, "/dev/timerfd couldn't create minor node");
+ ddi_soft_state_fini(&timerfd_softstate);
+ mutex_exit(&timerfd_lock);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ timerfd_devi = devi;
+
+ timerfd_minor = vmem_create("timerfd_minor", (void *)TIMERFDMNRN_CLONE,
+ UINT32_MAX - TIMERFDMNRN_CLONE, 1, NULL, NULL, NULL, 0,
+ VM_SLEEP | VMC_IDENTIFIER);
+
+ mutex_exit(&timerfd_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&timerfd_lock);
+ vmem_destroy(timerfd_minor);
+
+ ddi_remove_minor_node(timerfd_devi, NULL);
+ timerfd_devi = NULL;
+
+ ddi_soft_state_fini(&timerfd_softstate);
+ mutex_exit(&timerfd_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+timerfd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)timerfd_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+static struct cb_ops timerfd_cb_ops = {
+ timerfd_open, /* open */
+ timerfd_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ timerfd_read, /* read */
+ nodev, /* write */
+ timerfd_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ timerfd_poll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops timerfd_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ timerfd_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ timerfd_attach, /* attach */
+ timerfd_detach, /* detach */
+ nodev, /* reset */
+ &timerfd_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "timerfd support", /* name of module */
+ &timerfd_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/io/timerfd.conf b/usr/src/uts/common/io/timerfd.conf
new file mode 100644
index 0000000000..c6ad86d051
--- /dev/null
+++ b/usr/src/uts/common/io/timerfd.conf
@@ -0,0 +1,16 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015 Joyent, Inc. All rights reserved.
+#
+
+name="timerfd" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/os/clock_highres.c b/usr/src/uts/common/os/clock_highres.c
index bcdf20c0bd..805813037d 100644
--- a/usr/src/uts/common/os/clock_highres.c
+++ b/usr/src/uts/common/os/clock_highres.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2012, Joyent Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent Inc. All rights reserved.
*/
#include <sys/timer.h>
@@ -66,7 +66,7 @@ clock_highres_getres(timespec_t *ts)
/*ARGSUSED*/
static int
-clock_highres_timer_create(itimer_t *it, struct sigevent *ev)
+clock_highres_timer_create(itimer_t *it, void (*fire)(itimer_t *))
{
/*
* CLOCK_HIGHRES timers of sufficiently high resolution can deny
@@ -80,6 +80,7 @@ clock_highres_timer_create(itimer_t *it, struct sigevent *ev)
}
it->it_arg = kmem_zalloc(sizeof (cyclic_id_t), KM_SLEEP);
+ it->it_fire = fire;
return (0);
}
@@ -95,7 +96,7 @@ clock_highres_fire(void *arg)
old = *addr;
} while (atomic_cas_64((uint64_t *)addr, old, new) != old);
- timer_fire(it);
+ it->it_fire(it);
}
static int
diff --git a/usr/src/uts/common/os/clock_realtime.c b/usr/src/uts/common/os/clock_realtime.c
index ef3383fb28..4a75984b23 100644
--- a/usr/src/uts/common/os/clock_realtime.c
+++ b/usr/src/uts/common/os/clock_realtime.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015, Joyent Inc. All rights reserved.
+ */
#include <sys/timer.h>
#include <sys/systm.h>
@@ -80,8 +82,7 @@ clock_realtime_fire(void *arg)
/*
* First call into the timer subsystem to get the signal going.
*/
- timer_fire(it);
-
+ it->it_fire(it);
val = &it->it_itime.it_value;
interval = &it->it_itime.it_interval;
@@ -171,9 +172,10 @@ clock_realtime_fire_first(void *arg)
/*ARGSUSED*/
static int
-clock_realtime_timer_create(itimer_t *it, struct sigevent *ev)
+clock_realtime_timer_create(itimer_t *it, void (*fire)(itimer_t *))
{
it->it_arg = kmem_zalloc(sizeof (timeout_id_t), KM_SLEEP);
+ it->it_fire = fire;
return (0);
}
@@ -184,7 +186,7 @@ clock_realtime_timer_settime(itimer_t *it, int flags,
{
timeout_id_t tid, *tidp = it->it_arg;
timespec_t now;
- proc_t *p = curproc;
+ proc_t *p = it->it_proc;
clock_t ticks;
gethrestime(&now);
@@ -246,7 +248,7 @@ static int
clock_realtime_timer_gettime(itimer_t *it, struct itimerspec *when)
{
timespec_t now;
- proc_t *p = curproc;
+ proc_t *p = it->it_proc;
/*
* We always keep it_itime up to date, so we just need to snapshot
@@ -276,7 +278,7 @@ clock_realtime_timer_gettime(itimer_t *it, struct itimerspec *when)
static int
clock_realtime_timer_delete(itimer_t *it)
{
- proc_t *p = curproc;
+ proc_t *p = it->it_proc;
timeout_id_t tid, *tidp = it->it_arg;
mutex_enter(&p->p_lock);
diff --git a/usr/src/uts/common/os/timer.c b/usr/src/uts/common/os/timer.c
index 8559d8736c..b25a6cbcf1 100644
--- a/usr/src/uts/common/os/timer.c
+++ b/usr/src/uts/common/os/timer.c
@@ -269,6 +269,15 @@ clock_add_backend(clockid_t clock, clock_backend_t *backend)
clock_backend[clock] = backend;
}
+clock_backend_t *
+clock_get_backend(clockid_t clock)
+{
+ if (clock < 0 || clock >= CLOCK_MAX)
+ return (NULL);
+
+ return (clock_backend[clock]);
+}
+
int
clock_settime(clockid_t clock, timespec_t *tp)
{
@@ -398,7 +407,7 @@ timer_signal(sigqueue_t *sigq)
/*
* This routine is called from the clock backend.
*/
-void
+static void
timer_fire(itimer_t *it)
{
proc_t *p;
@@ -672,7 +681,7 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
* Call on the backend to verify the event argument (or return
* EINVAL if this clock type does not support timers).
*/
- if ((error = backend->clk_timer_create(it, &ev)) != 0)
+ if ((error = backend->clk_timer_create(it, timer_fire)) != 0)
goto err;
it->it_lwp = ttolwp(curthread);
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index c2bf2f0483..39288d5cc0 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -587,6 +587,7 @@ CHKHDRS= \
time_std_impl.h \
timeb.h \
timer.h \
+ timerfd.h \
times.h \
timex.h \
timod.h \
diff --git a/usr/src/uts/common/sys/timer.h b/usr/src/uts/common/sys/timer.h
index 604ddf5d83..ec349c962f 100644
--- a/usr/src/uts/common/sys/timer.h
+++ b/usr/src/uts/common/sys/timer.h
@@ -24,6 +24,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#ifndef _SYS_TIMER_H
#define _SYS_TIMER_H
@@ -55,32 +59,45 @@ extern int timer_max; /* patchable via /etc/system */
struct clock_backend;
-typedef struct itimer {
+struct itimer;
+typedef struct itimer itimer_t;
+
+struct itimer {
itimerspec_t it_itime;
hrtime_t it_hrtime;
ushort_t it_flags;
ushort_t it_lock;
- void *it_arg;
- sigqueue_t *it_sigq;
- klwp_t *it_lwp;
+ void *it_arg; /* clock backend-specific data */
struct proc *it_proc;
+ union {
+ struct {
+ sigqueue_t *__it_sigq;
+ klwp_t *__it_lwp;
+ } __proc;
+ void *__it_frontend;
+ } __data; /* timer frontend-specific data */
kcondvar_t it_cv;
int it_blockers;
int it_pending;
int it_overrun;
struct clock_backend *it_backend;
+ void (*it_fire)(itimer_t *);
kmutex_t it_mutex;
void *it_portev; /* port_kevent_t pointer */
void *it_portsrc; /* port_source_t pointer */
int it_portfd; /* port file descriptor */
-} itimer_t;
+};
+
+#define it_sigq __data.__proc.__it_sigq
+#define it_lwp __data.__proc.__it_lwp
+#define it_frontend __data.__it_frontend
typedef struct clock_backend {
struct sigevent clk_default;
int (*clk_clock_settime)(timespec_t *);
int (*clk_clock_gettime)(timespec_t *);
int (*clk_clock_getres)(timespec_t *);
- int (*clk_timer_create)(itimer_t *, struct sigevent *);
+ int (*clk_timer_create)(itimer_t *, void (*)(itimer_t *));
int (*clk_timer_settime)(itimer_t *, int, const struct itimerspec *);
int (*clk_timer_gettime)(itimer_t *, struct itimerspec *);
int (*clk_timer_delete)(itimer_t *);
@@ -88,8 +105,8 @@ typedef struct clock_backend {
} clock_backend_t;
extern void clock_add_backend(clockid_t clock, clock_backend_t *backend);
+extern clock_backend_t *clock_get_backend(clockid_t clock);
-extern void timer_fire(itimer_t *);
extern void timer_lwpbind();
extern void timer_func(sigqueue_t *);
diff --git a/usr/src/uts/common/sys/timerfd.h b/usr/src/uts/common/sys/timerfd.h
new file mode 100644
index 0000000000..66cb50ac88
--- /dev/null
+++ b/usr/src/uts/common/sys/timerfd.h
@@ -0,0 +1,81 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Header file to support for the timerfd facility.
+ */
+
+#ifndef _SYS_TIMERFD_H
+#define _SYS_TIMERFD_H
+
+#include <sys/types.h>
+#include <sys/time_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * To assure binary compatibility with Linux, these values are fixed at their
+ * Linux equivalents, not their native ones.
+ */
+#define TFD_CLOEXEC 02000000 /* LX_O_CLOEXEC */
+#define TFD_NONBLOCK 04000 /* LX_O_NONBLOCK */
+#define TFD_TIMER_ABSTIME (1 << 0)
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+
+/*
+ * These ioctl values are specific to the native implementation; applications
+ * shouldn't be using them directly, and they should therefore be safe to
+ * change without breaking apps.
+ */
+#define TIMERFDIOC (('t' << 24) | ('f' << 16) | ('d' << 8))
+#define TIMERFDIOC_CREATE (TIMERFDIOC | 1) /* create timer */
+#define TIMERFDIOC_SETTIME (TIMERFDIOC | 2) /* timerfd_settime() */
+#define TIMERFDIOC_GETTIME (TIMERFDIOC | 3) /* timerfd_gettime() */
+
+typedef struct timerfd_settime {
+ uint64_t tfd_settime_flags; /* flags (e.g., TFD_TIMER_ABSTIME) */
+ uint64_t tfd_settime_value; /* pointer to value */
+ uint64_t tfd_settime_ovalue; /* pointer to old value, if any */
+} timerfd_settime_t;
+
+#ifndef _KERNEL
+
+extern int timerfd_create(int, int);
+extern int timerfd_settime(int, int,
+ const struct itimerspec *, struct itimerspec *);
+extern int timerfd_gettime(int, struct itimerspec *);
+
+#else
+
+#define TIMERFDMNRN_TIMERFD 0
+#define TIMERFDMNRN_CLONE 1
+#define TIMERFD_VALMAX (ULLONG_MAX - 1ULL)
+
+/*
+ * Fortunately, the values for the Linux clocks that are valid for timerfd
+ * (namely, CLOCK_REALTIME and CLOCK_MONOTONIC) don't overlap with our values
+ * the same.
+ */
+#define TIMERFD_MONOTONIC 1 /* Linux value for CLOCK_MONOTONIC */
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_TIMERFD_H */
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index 64b4962cae..18d05f23d0 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -21,6 +21,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2015 Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
#
# This makefile contains the common definitions for all intel
@@ -338,6 +339,7 @@ DRV_KMODS += sysevent
DRV_KMODS += sysmsg
DRV_KMODS += tcp
DRV_KMODS += tcp6
+DRV_KMODS += timerfd
DRV_KMODS += tl
DRV_KMODS += tnf
DRV_KMODS += tpm
diff --git a/usr/src/uts/intel/timerfd/Makefile b/usr/src/uts/intel/timerfd/Makefile
new file mode 100644
index 0000000000..28cf8fd41e
--- /dev/null
+++ b/usr/src/uts/intel/timerfd/Makefile
@@ -0,0 +1,68 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = timerfd
+OBJECTS = $(TIMERFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(TIMERFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CERRWARN += -_gcc=-Wno-parentheses
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sparc/Makefile.sparc b/usr/src/uts/sparc/Makefile.sparc
index d7c45a396e..29e4cd7ccf 100644
--- a/usr/src/uts/sparc/Makefile.sparc
+++ b/usr/src/uts/sparc/Makefile.sparc
@@ -21,6 +21,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
diff --git a/usr/src/uts/sparc/timerfd/Makefile b/usr/src/uts/sparc/timerfd/Makefile
new file mode 100644
index 0000000000..969ca6dd1a
--- /dev/null
+++ b/usr/src/uts/sparc/timerfd/Makefile
@@ -0,0 +1,68 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = timerfd
+OBJECTS = $(TIMERFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(TIMERFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+CERRWARN += -_gcc=-Wno-parentheses
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ