Changeset View
Standalone View
sys/kern/sys_timerfd.c
- This file was added.
/*- | ||||||||||||||||||||||||||
* SPDX-License-Identifier: BSD-2-Clause | ||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||
* Copyright (c) 2014 Dmitry Chagin <dchagin@FreeBSD.org> | ||||||||||||||||||||||||||
* Copyright (c) 2023 Jake Freeland <jfree@FreeBSD.org> | ||||||||||||||||||||||||||
dchagin: should be removed, Roman was involved in the epoll part
| ||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||
* Redistribution and use in source and binary forms, with or without | ||||||||||||||||||||||||||
* modification, are permitted provided that the following conditions | ||||||||||||||||||||||||||
* are met: | ||||||||||||||||||||||||||
* 1. Redistributions of source code must retain the above copyright | ||||||||||||||||||||||||||
* notice, this list of conditions and the following disclaimer. | ||||||||||||||||||||||||||
* 2. Redistributions in binary form must reproduce the above copyright | ||||||||||||||||||||||||||
* notice, this list of conditions and the following disclaimer in the | ||||||||||||||||||||||||||
* documentation and/or other materials provided with the distribution. | ||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||||||||||||||||||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||||||||||||||||||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||||||||||||||||||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||||||||||||||||||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||||||||||||||||||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||||||||||||||||||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||||||||||||||||||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||||||||||||||||||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||||||||||||||||||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||||||||||||||||||||||
* SUCH DAMAGE. | ||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||
#include <sys/param.h> | ||||||||||||||||||||||||||
#include <sys/systm.h> | ||||||||||||||||||||||||||
#include <sys/callout.h> | ||||||||||||||||||||||||||
Done Inline ActionsDid that came back? I do not remember if I asked to remove the $FreeBSD$ from the new files. kib: Did that came back? I do not remember if I asked to remove the $FreeBSD$ from the new files. | ||||||||||||||||||||||||||
Done Inline Actions
You asked on https://reviews.freebsd.org/D38460. I was unsure if it was still required for kernel files, so I just left it in here. I take it that __FSDID() is obsolete everywhere in the source tree? jfree: > Did that came back? I do not remember if I asked to remove the $FreeBSD$ from the new files. | ||||||||||||||||||||||||||
Not Done Inline ActionsCurrent policy is to not introduce these into new files, but also not to remove existing IDs. The idea was that a single sweep should get rid of them. Might be, some day. kib: Current policy is to not introduce these into new files, but also not to remove existing IDs. | ||||||||||||||||||||||||||
#include <sys/fcntl.h> | ||||||||||||||||||||||||||
#include <sys/file.h> | ||||||||||||||||||||||||||
#include <sys/filedesc.h> | ||||||||||||||||||||||||||
#include <sys/filio.h> | ||||||||||||||||||||||||||
#include <sys/kernel.h> | ||||||||||||||||||||||||||
#include <sys/lock.h> | ||||||||||||||||||||||||||
#include <sys/malloc.h> | ||||||||||||||||||||||||||
#include <sys/mutex.h> | ||||||||||||||||||||||||||
#include <sys/poll.h> | ||||||||||||||||||||||||||
#include <sys/proc.h> | ||||||||||||||||||||||||||
#include <sys/queue.h> | ||||||||||||||||||||||||||
#include <sys/selinfo.h> | ||||||||||||||||||||||||||
#include <sys/stat.h> | ||||||||||||||||||||||||||
#include <sys/sysctl.h> | ||||||||||||||||||||||||||
#include <sys/sysent.h> | ||||||||||||||||||||||||||
#include <sys/sysproto.h> | ||||||||||||||||||||||||||
#include <sys/timerfd.h> | ||||||||||||||||||||||||||
#include <sys/timespec.h> | ||||||||||||||||||||||||||
#include <sys/uio.h> | ||||||||||||||||||||||||||
#include <sys/user.h> | ||||||||||||||||||||||||||
#include <security/audit/audit.h> | ||||||||||||||||||||||||||
Done Inline Actions
brooks: | ||||||||||||||||||||||||||
#ifdef COMPAT_FREEBSD32 | ||||||||||||||||||||||||||
#include <compat/freebsd32/freebsd32.h> | ||||||||||||||||||||||||||
Done Inline ActionsBTW, could this define be static? kib: BTW, could this define be static? | ||||||||||||||||||||||||||
#include <compat/freebsd32/freebsd32_proto.h> | ||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||
static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures"); | ||||||||||||||||||||||||||
static LIST_HEAD(, timerfd) timerfd_head; | ||||||||||||||||||||||||||
static struct unrhdr64 tfdino_unr; | ||||||||||||||||||||||||||
#define TFD_NOJUMP 0 | ||||||||||||||||||||||||||
#define TFD_READ 1 | ||||||||||||||||||||||||||
#define TFD_ZREAD 2 | ||||||||||||||||||||||||||
#define TFD_CANCELED 4 | ||||||||||||||||||||||||||
#define TFD_JUMPED (TFD_ZREAD | TFD_CANCELED) | ||||||||||||||||||||||||||
struct timerfd { | ||||||||||||||||||||||||||
/* User specified. */ | ||||||||||||||||||||||||||
struct itimerspec tfd_time; /* tfd timer */ | ||||||||||||||||||||||||||
clockid_t tfd_clockid; /* timing base */ | ||||||||||||||||||||||||||
int tfd_flags; /* creation flags */ | ||||||||||||||||||||||||||
int tfd_timflags; /* timer flags */ | ||||||||||||||||||||||||||
int tfd_jumped; /* timer jump status */ | ||||||||||||||||||||||||||
Done Inline ActionsWhen using designated initializers for structs, please put the ending comma at all lines. kib: When using designated initializers for structs, please put the ending comma at all lines. | ||||||||||||||||||||||||||
Done Inline Actions
style(9) has an example of a designated initializer that doesn't have a comma at the end. Looking at other source files, I appear to be finding more instances of no comma at the end, as well. I am going to leave this style choice how it is. jfree: > When using designated initializers for structs, please put the ending comma at all lines. | ||||||||||||||||||||||||||
Done Inline ActionsI agree with kib on this one. We aren't generally going around churning the repo to update things, but this prevents future churn when adding new entries. brooks: I agree with kib on this one. We aren't generally going around churning the repo to update… | ||||||||||||||||||||||||||
/* Used internally. */ | ||||||||||||||||||||||||||
timerfd_t tfd_count; /* expiration count since last read */ | ||||||||||||||||||||||||||
bool tfd_expired; /* true upon initial expiration */ | ||||||||||||||||||||||||||
struct mtx tfd_lock; /* mtx lock */ | ||||||||||||||||||||||||||
struct callout tfd_callout; /* expiration notification */ | ||||||||||||||||||||||||||
struct selinfo tfd_sel; /* I/O alerts */ | ||||||||||||||||||||||||||
struct timespec tfd_boottim; /* cached boottime */ | ||||||||||||||||||||||||||
LIST_ENTRY(timerfd) entry; /* entry in list */ | ||||||||||||||||||||||||||
Done Inline ActionsThere too kib: There too | ||||||||||||||||||||||||||
/* For stat(2). */ | ||||||||||||||||||||||||||
ino_t tfd_ino; /* inode number */ | ||||||||||||||||||||||||||
struct timespec tfd_atim; /* time of last read */ | ||||||||||||||||||||||||||
struct timespec tfd_mtim; /* time of last settime */ | ||||||||||||||||||||||||||
struct timespec tfd_birthtim; /* creation time */ | ||||||||||||||||||||||||||
Done Inline ActionsAnd where is this unr initialized? kib: And where is this unr initialized? | ||||||||||||||||||||||||||
Done Inline Actions
new_unrhdr64() in timerfd_create_file(). jfree: > And where is this unr initialized?
`new_unrhdr64()` in `timerfd_create_file()`. | ||||||||||||||||||||||||||
Done Inline ActionsThis still does not make a sense. The unrhdr64 var needs to be initialized once, globally. Instead you re-initialize it on each open. kib: This still does not make a sense. The unrhdr64 var needs to be initialized once, globally. | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
static void | ||||||||||||||||||||||||||
timerfd_init(void *data) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
new_unrhdr64(&tfdino_unr, 1); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
SYSINIT(timerfd, SI_SUB_VFS, SI_ORDER_ANY, timerfd_init, NULL); | ||||||||||||||||||||||||||
static inline void | ||||||||||||||||||||||||||
timerfd_getboottime(struct timespec *ts) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timeval tv; | ||||||||||||||||||||||||||
getboottime(&tv); | ||||||||||||||||||||||||||
TIMEVAL_TO_TIMESPEC(&tv, ts); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||
* Call when a discontinuous jump has occured in CLOCK_REALTIME and | ||||||||||||||||||||||||||
* update timerfd's cached boottime. A jump can be triggered using | ||||||||||||||||||||||||||
* functions like clock_settime(2) or settimeofday(2). | ||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||
* Timer is marked TFD_CANCELED if TFD_TIMER_CANCEL_ON_SET is set | ||||||||||||||||||||||||||
* and the realtime clock jumps. | ||||||||||||||||||||||||||
* Timer is marked TFD_ZREAD if TFD_TIMER_CANCEL_ON_SET is not set, | ||||||||||||||||||||||||||
Done Inline ActionsWhy? This must be a static data, so that each timerfd gets unique ino. Right now you are generating the same ino for each timerfd. kib: Why? This must be a static data, so that each timerfd gets unique ino. Right now you are… | ||||||||||||||||||||||||||
* but the realtime clock jumps backwards. | ||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||
void | ||||||||||||||||||||||||||
timerfd_jumped(void) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
Done Inline ActionsIf you put SYSINIT right after the timerfd_init(), you can avoid forward-declare the function. kib: If you put SYSINIT right after the timerfd_init(), you can avoid forward-declare the function. | ||||||||||||||||||||||||||
struct timerfd *tfd; | ||||||||||||||||||||||||||
struct timespec boottime, diff; | ||||||||||||||||||||||||||
timerfd_getboottime(&boottime); | ||||||||||||||||||||||||||
LIST_FOREACH(tfd, &timerfd_head, entry) { | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
if (tfd->tfd_clockid != CLOCK_REALTIME || | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
(tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 || | ||||||||||||||||||||||||||
timespeccmp(&boottime, &tfd->tfd_boottim, ==)) { | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Done Inline ActionsThis overrides previously set O_CLOEXEC bit kib: This overrides previously set O_CLOEXEC bit | ||||||||||||||||||||||||||
Done Inline ActionsWhy do you directly use O_ namespace there, instead of TFD_ alias? kib: Why do you directly use O_ namespace there, instead of TFD_ alias? | ||||||||||||||||||||||||||
if (callout_active(&tfd->tfd_callout)) { | ||||||||||||||||||||||||||
if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0) | ||||||||||||||||||||||||||
tfd->tfd_jumped = TFD_CANCELED; | ||||||||||||||||||||||||||
Done Inline ActionsIf there is not free_unr64(), then alloc_unr64() is just a very slow atomic_add_64(). It seems pipes have the same issue. kib: If there is not free_unr64(), then alloc_unr64() is just a very slow atomic_add_64(). It seems… | ||||||||||||||||||||||||||
Done Inline Actions
Yes, a very slow atomic_fetchadd_64(). I was curious about this as well. I figured it was probably best to keep the unr64 interface in timerfd just in case the underlying code changes in the future. jfree: > If there is not free_unr64(), then alloc_unr64() is just a very slow atomic_add_64(). It… | ||||||||||||||||||||||||||
Done Inline ActionsNo, it is not too slow to execute single fetchadd for timerfd instantiation. I was wrong in that I forgot that unr64 has nothing to do with real unr which tracks allocations. kib: No, it is not too slow to execute single fetchadd for timerfd instantiation. I was wrong in… | ||||||||||||||||||||||||||
else if (timespeccmp(&boottime, &tfd->tfd_boottim, <)) | ||||||||||||||||||||||||||
tfd->tfd_jumped = TFD_ZREAD; | ||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||
* Do not reschedule callout when | ||||||||||||||||||||||||||
* inside interval time loop. | ||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||
if (!tfd->tfd_expired) { | ||||||||||||||||||||||||||
timespecsub(&boottime, | ||||||||||||||||||||||||||
&tfd->tfd_boottim, &diff); | ||||||||||||||||||||||||||
timespecsub(&tfd->tfd_time.it_value, | ||||||||||||||||||||||||||
&diff, &tfd->tfd_time.it_value); | ||||||||||||||||||||||||||
if (callout_stop(&tfd->tfd_callout) == 1) { | ||||||||||||||||||||||||||
callout_schedule_sbt(&tfd->tfd_callout, | ||||||||||||||||||||||||||
tstosbt(tfd->tfd_time.it_value), | ||||||||||||||||||||||||||
0, C_ABSOLUTE); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
tfd->tfd_boottim = boottime; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
Done Inline ActionsI am curious why do you check for these conditions, there and in all other places. How is it possible for either of the check to be true? kib: I am curious why do you check for these conditions, there and in all other places. How is it… | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred, | ||||||||||||||||||||||||||
int flags, struct thread *td) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
timerfd_t count; | ||||||||||||||||||||||||||
int error = 0; | ||||||||||||||||||||||||||
if (uio->uio_resid < sizeof(timerfd_t)) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
retry: | ||||||||||||||||||||||||||
getnanotime(&tfd->tfd_atim); | ||||||||||||||||||||||||||
if ((tfd->tfd_jumped & TFD_JUMPED) != 0) { | ||||||||||||||||||||||||||
if (tfd->tfd_jumped == TFD_CANCELED) | ||||||||||||||||||||||||||
error = ECANCELED; | ||||||||||||||||||||||||||
tfd->tfd_jumped = TFD_READ; | ||||||||||||||||||||||||||
tfd->tfd_count = 0; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
tfd->tfd_jumped = TFD_NOJUMP; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
if (tfd->tfd_count == 0) { | ||||||||||||||||||||||||||
if ((fp->f_flag & FNONBLOCK) != 0) { | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (EAGAIN); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
td->td_rtcgen = atomic_load_acq_int(&rtc_generation); | ||||||||||||||||||||||||||
error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock, | ||||||||||||||||||||||||||
PCATCH, "tfdrd", 0); | ||||||||||||||||||||||||||
if (error == 0) { | ||||||||||||||||||||||||||
goto retry; | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
count = tfd->tfd_count; | ||||||||||||||||||||||||||
tfd->tfd_count = 0; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
error = uiomove(&count, sizeof(timerfd_t), uio); | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_ioctl(struct file *fp, u_long cmd, void *data, | ||||||||||||||||||||||||||
struct ucred *active_cred, struct thread *td) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
switch (cmd) { | ||||||||||||||||||||||||||
case FIOASYNC: | ||||||||||||||||||||||||||
if (*(int *)data != 0) | ||||||||||||||||||||||||||
atomic_set_int(&fp->f_flag, FASYNC); | ||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||
atomic_clear_int(&fp->f_flag, FASYNC); | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
case FIONBIO: | ||||||||||||||||||||||||||
if (*(int *)data != 0) | ||||||||||||||||||||||||||
atomic_set_int(&fp->f_flag, FNONBLOCK); | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
atomic_clear_int(&fp->f_flag, FNONBLOCK); | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
return (ENOTTY); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_poll(struct file *fp, int events, struct ucred *active_cred, | ||||||||||||||||||||||||||
struct thread *td) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
int revents = 0; | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
if ((events & (POLLIN | POLLRDNORM)) != 0 && | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ) | ||||||||||||||||||||||||||
revents |= events & (POLLIN | POLLRDNORM); | ||||||||||||||||||||||||||
if (revents == 0) | ||||||||||||||||||||||||||
selrecord(td, &tfd->tfd_sel); | ||||||||||||||||||||||||||
Done Inline ActionsThe leave just this line alone, removing if() and else kib: The leave just this line alone, removing if() and else | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (revents); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static void | ||||||||||||||||||||||||||
filt_timerfddetach(struct knote *kn) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = kn->kn_hook; | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
knlist_remove(&tfd->tfd_sel.si_note, kn, 1); | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
filt_timerfdread(struct knote *kn, long hint) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = kn->kn_hook; | ||||||||||||||||||||||||||
return (tfd->tfd_count > 0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static struct filterops timerfd_rfiltops = { | ||||||||||||||||||||||||||
Done Inline Actions
brooks: | ||||||||||||||||||||||||||
.f_isfd = 1, | ||||||||||||||||||||||||||
.f_detach = filt_timerfddetach, | ||||||||||||||||||||||||||
.f_event = filt_timerfdread, | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_kqfilter(struct file *fp, struct knote *kn) | ||||||||||||||||||||||||||
Done Inline Actions
brooks: | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
if (kn->kn_filter != EVFILT_READ) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
kn->kn_fop = &timerfd_rfiltops; | ||||||||||||||||||||||||||
Done Inline Actions
Also remove the other TFD_GETTIME32 case. The _Static_assert isn't strictly necessary. brooks: Also remove the other TFD_GETTIME32 case.
The _Static_assert isn't strictly necessary. | ||||||||||||||||||||||||||
Done Inline ActionsCould you explain why we CAN depend on the command differing for TFD_SETTIME? I understand that the 32/64 ioctl() variants have args of differing sizes, but wouldn't any machine just use the TFD_SETTIME case given that libc passes that in? I don't see the control flow saying "if 32-bit, then use the 32-bit variants". jfree: Could you explain why we CAN depend on the command differing for `TFD_SETTIME`? I understand… | ||||||||||||||||||||||||||
Done Inline Actions
TFD_SETTIME is defined with the size of struct timerfd_settime_args, but that varies with the compiled ABI. When a 32-bit program is compiled that size is for the version with 32-bit pointers which is equivalent to struct timerfd_settime_args32. brooks: > Could you explain why we CAN depend on the command differing for `TFD_SETTIME`? I understand… | ||||||||||||||||||||||||||
Done Inline Actions
Perhaps you already answered this and I am misinterpreting, but I was trying to figure out why 32-bit binaries choose the TFD_GETTIME32 ioctl case when TFD_GETTIME (non-32) is passed in via libc? jfree: > TFD_SETTIME is defined with the size of `struct timerfd_settime_args`, but that varies with… | ||||||||||||||||||||||||||
Done Inline ActionsThe ioctl command values are obtained by some combination of three attributes: read/write mask, the io command itself, and the length of the parameter' structure. The details are provided in sys/ioccom.h. Now consider two compilation environments for userspace: ILP32 (32bit) and LP64 (64bit). The mask and the io command are same, but generally the parameter' structure sizes are different for 32 vs 64 bit. This means that the final value of the ioctl command passed to kernel should be typically different between 32 vs 64bit user space. Ultimately, this allows the kernel to distinguish the bitness of the ioctl caller without looking at ABI (AKA sysentvec). But if it happens that the parameter's structure layout is same for 32 and 64 bit ABIs, the resulting ioctl commands would have exactly the same numeric values. Then kernel could not distinguish the mode of caller. If parameters do not contain pointers, it is fine to leave only one handler for the command. But if there are pointers, you need to properly reconstruct usermode addresses. This is where ABI checks are needed. kib: The ioctl command values are obtained by some combination of three attributes: read/write mask… | ||||||||||||||||||||||||||
Done Inline ActionsThank you for writing this. I understand now :) jfree: Thank you for writing this. I understand now :) | ||||||||||||||||||||||||||
kn->kn_hook = tfd; | ||||||||||||||||||||||||||
knlist_add(&tfd->tfd_sel.si_note, kn, 0); | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
Done Inline ActionsHow could it be that TFD_GETTIME32 is not defined? kib: How could it be that TFD_GETTIME32 is not defined? | ||||||||||||||||||||||||||
Done Inline ActionsI think it should only be defined on amd64 because the size of the argument only differs between i386 and everything else. If we define it on non-amd64 we'll have a duplicate case value which is an error. brooks: I think it should only be defined on amd64 because the size of the argument only differs… | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Done Inline ActionsI do not see much use for this assert, esp. because I believe this code is never compiled. kib: I do not see much use for this assert, esp. because I believe this code is never compiled. | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) | ||||||||||||||||||||||||||
Done Inline ActionsBTW, is there a useful interpretation of the fstat(2) fields for timerfd? Does Linux return ENXIO for fstat(timerfd)? kib: BTW, is there a useful interpretation of the fstat(2) fields for timerfd? Does Linux return… | ||||||||||||||||||||||||||
Done Inline Actions
I did some testing and it turns out that timerfd fds have inodes attached to them in Linux. Their fstat() output looks something like this: ID: 14 INODE: 2084 MODE: 384 NLINK: 1 UID: 0 GID: 0 RDEV: 0 SIZE: 0 BLKSIZE: 4096 BLOCKS: 0 I attempted doing a VOP_STAT() in timerfd_stat(), but quickly realized that a vnode is never initialized in this implementation. As a result, fp->f_vnode is NULL. Not sure whether I should bother allocating a vnode to replicate the Linux behavior. jfree: > BTW, is there a useful interpretation of the fstat(2) fields for timerfd? Does Linux return… | ||||||||||||||||||||||||||
Done Inline ActionsThere is no need to allocate a vnode, your file type is not a vnode' anyway. Implement the fo_stat() method for the file. kib: There is no need to allocate a vnode, your file type is not a vnode' anyway. Implement the… | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
bzero(sb, sizeof(*sb)); | ||||||||||||||||||||||||||
sb->st_nlink = fp->f_count - 1; | ||||||||||||||||||||||||||
sb->st_uid = fp->f_cred->cr_uid; | ||||||||||||||||||||||||||
sb->st_gid = fp->f_cred->cr_gid; | ||||||||||||||||||||||||||
sb->st_blksize = PAGE_SIZE; | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
sb->st_ino = tfd->tfd_ino; | ||||||||||||||||||||||||||
sb->st_atim = tfd->tfd_atim; | ||||||||||||||||||||||||||
sb->st_mtim = tfd->tfd_mtim; | ||||||||||||||||||||||||||
sb->st_birthtim = tfd->tfd_birthtim; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Done Inline ActionsAre you sure that this is the right implementation for these FIO* iocontrols? FIOASYNC, if implemented, should change async mode, if FIONBIO reports that async is supported. Also, what about FIONREAD? kib: Are you sure that this is the right implementation for these FIO* iocontrols? FIOASYNC, if… | ||||||||||||||||||||||||||
Done Inline Actions
These are remnants from @dchagin's lkpi tfd implementation. I am not sure why they are here as the tfd Linux man page does not mention explicit support for them. I can't really see an easy way to implement these generic ioctls and don't think they're necessary. What're your thoughts? jfree: > Are you sure that this is the right implementation for these FIO* iocontrols? FIOASYNC, if… | ||||||||||||||||||||||||||
Done Inline ActionsI believe it is not hard to implement them. You just need to understand the semantic. But having the dummy placeholders returning success is wrong any way. kib: I believe it is not hard to implement them. You just need to understand the semantic.
But… | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_close(struct file *fp, struct thread *td) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
Done Inline ActionsWhat if more than one thread do the ioctl in parallel? Isn't it racy? kib: What if more than one thread do the ioctl in parallel? Isn't it racy? | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
callout_drain(&tfd->tfd_callout); | ||||||||||||||||||||||||||
Done Inline ActionsThe value should be returned only when read would not block, otherwise you should return 0. kib: The value should be returned only when read would not block, otherwise you should return 0. | ||||||||||||||||||||||||||
seldrain(&tfd->tfd_sel); | ||||||||||||||||||||||||||
knlist_destroy(&tfd->tfd_sel.si_note); | ||||||||||||||||||||||||||
Done Inline ActionsIMO it makes sense to generate unique 'inode id' for timerfd, I suspect some Linux code might depend on the feature. Look at the sys_pipe.c' for pipe_ino, for example of how this is done. kib: IMO it makes sense to generate unique 'inode id' for timerfd, I suspect some Linux code might… | ||||||||||||||||||||||||||
mtx_destroy(&tfd->tfd_lock); | ||||||||||||||||||||||||||
LIST_REMOVE(tfd, entry); | ||||||||||||||||||||||||||
free(tfd, M_TIMERFD); | ||||||||||||||||||||||||||
fp->f_ops = &badfileops; | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
Done Inline ActionsThis expression is never true. kib: This expression is never true. | ||||||||||||||||||||||||||
Done Inline Actions
Oops. Lapse in my logic late at night to blame. I meant != 0. jfree: > This expression is never true.
Oops. Lapse in my logic late at night to blame. I meant `!=… | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Done Inline ActionsYou should only return non-zero *data when read would not block. It has nothing to do with non-blocked mode. kib: You should only return non-zero *data when read would not block. It has nothing to do with non… | ||||||||||||||||||||||||||
static int | ||||||||||||||||||||||||||
timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, | ||||||||||||||||||||||||||
struct filedesc *fdp) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = fp->f_data; | ||||||||||||||||||||||||||
kif->kf_type = KF_TYPE_TIMERFD; | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid; | ||||||||||||||||||||||||||
kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags; | ||||||||||||||||||||||||||
kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static struct fileops timerfdops = { | ||||||||||||||||||||||||||
.fo_read = timerfd_read, | ||||||||||||||||||||||||||
.fo_write = invfo_rdwr, | ||||||||||||||||||||||||||
.fo_truncate = invfo_truncate, | ||||||||||||||||||||||||||
.fo_ioctl = timerfd_ioctl, | ||||||||||||||||||||||||||
.fo_poll = timerfd_poll, | ||||||||||||||||||||||||||
.fo_kqfilter = timerfd_kqfilter, | ||||||||||||||||||||||||||
.fo_stat = timerfd_stat, | ||||||||||||||||||||||||||
.fo_close = timerfd_close, | ||||||||||||||||||||||||||
.fo_chmod = invfo_chmod, | ||||||||||||||||||||||||||
.fo_chown = invfo_chown, | ||||||||||||||||||||||||||
.fo_sendfile = invfo_sendfile, | ||||||||||||||||||||||||||
.fo_fill_kinfo = timerfd_fill_kinfo, | ||||||||||||||||||||||||||
.fo_flags = DFLAG_PASSABLE, | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
static void | ||||||||||||||||||||||||||
timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timespec curr_value; | ||||||||||||||||||||||||||
*old_value = tfd->tfd_time; | ||||||||||||||||||||||||||
if (timespecisset(&tfd->tfd_time.it_value)) { | ||||||||||||||||||||||||||
nanouptime(&curr_value); | ||||||||||||||||||||||||||
timespecsub(&tfd->tfd_time.it_value, &curr_value, | ||||||||||||||||||||||||||
&old_value->it_value); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
static void | ||||||||||||||||||||||||||
timerfd_expire(void *arg) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct timerfd *tfd = (struct timerfd *)arg; | ||||||||||||||||||||||||||
struct timespec uptime; | ||||||||||||||||||||||||||
++tfd->tfd_count; | ||||||||||||||||||||||||||
tfd->tfd_expired = true; | ||||||||||||||||||||||||||
if (timespecisset(&tfd->tfd_time.it_interval)) { | ||||||||||||||||||||||||||
/* Count missed events. */ | ||||||||||||||||||||||||||
nanouptime(&uptime); | ||||||||||||||||||||||||||
if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) { | ||||||||||||||||||||||||||
timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime); | ||||||||||||||||||||||||||
tfd->tfd_count += tstosbt(uptime) / | ||||||||||||||||||||||||||
tstosbt(tfd->tfd_time.it_interval); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
timespecadd(&tfd->tfd_time.it_value, | ||||||||||||||||||||||||||
&tfd->tfd_time.it_interval, &tfd->tfd_time.it_value); | ||||||||||||||||||||||||||
callout_schedule_sbt(&tfd->tfd_callout, | ||||||||||||||||||||||||||
tstosbt(tfd->tfd_time.it_value), | ||||||||||||||||||||||||||
0, C_ABSOLUTE); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
/* Single shot timer. */ | ||||||||||||||||||||||||||
callout_deactivate(&tfd->tfd_callout); | ||||||||||||||||||||||||||
timespecclear(&tfd->tfd_time.it_value); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
wakeup(&tfd->tfd_count); | ||||||||||||||||||||||||||
selwakeup(&tfd->tfd_sel); | ||||||||||||||||||||||||||
KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Done Inline ActionsThis is inherited from the old code, but using hz here means that timerfd timers have quite low precision (1ms or 10ms by default). I think it'd be better to use tvtosbt() and callout_reset_sbt() instead. markj: This is inherited from the old code, but using `hz` here means that timerfd timers have quite… | ||||||||||||||||||||||||||
Done Inline ActionsI did some testing and sbt appears to be quite a bit less precise compared to hz. I simply modified the call to callout_reset_sbt(&tfd->tfd_callout, tvtosbt(tv), 0, timerfd_expire, tfd, 0);. Perhaps this is because I didn't specify C_PREL() in flags? I am unsure what the default precision for sbt is. jfree: I did some testing and `sbt` appears to be quite a bit less precise compared to `hz`. I simply… | ||||||||||||||||||||||||||
Done Inline ActionsNevermind now... I just recompiled with callout_reset(&tfd->tfd_callout, tvtohz(&tv), timerfd_expire, tfd); and I am experiencing the same latency. I must've created a delay in the code elsewhere. jfree: Nevermind now... I just recompiled with `callout_reset(&tfd->tfd_callout, tvtohz(&tv)… | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
kern_timerfd_create(struct thread *td, int clockid, int flags) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct file *fp; | ||||||||||||||||||||||||||
struct timerfd *tfd; | ||||||||||||||||||||||||||
int error, fd, fflags = 0; | ||||||||||||||||||||||||||
AUDIT_ARG_VALUE(clockid); | ||||||||||||||||||||||||||
AUDIT_ARG_FFLAGS(flags); | ||||||||||||||||||||||||||
Done Inline ActionsWrong indent. kib: Wrong indent. | ||||||||||||||||||||||||||
if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
if ((flags & TFD_CLOEXEC) != 0) | ||||||||||||||||||||||||||
fflags |= O_CLOEXEC; | ||||||||||||||||||||||||||
tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO); | ||||||||||||||||||||||||||
if (tfd == NULL) | ||||||||||||||||||||||||||
return (ENOMEM); | ||||||||||||||||||||||||||
tfd->tfd_clockid = (clockid_t)clockid; | ||||||||||||||||||||||||||
tfd->tfd_flags = flags; | ||||||||||||||||||||||||||
Done Inline Actionsmalloc M_WAITOK does not fail, so there is no point null checking.... mjg: malloc M_WAITOK does not fail, so there is no point null checking.... | ||||||||||||||||||||||||||
tfd->tfd_ino = alloc_unr64(&tfdino_unr); | ||||||||||||||||||||||||||
mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF); | ||||||||||||||||||||||||||
callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0); | ||||||||||||||||||||||||||
knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock); | ||||||||||||||||||||||||||
timerfd_getboottime(&tfd->tfd_boottim); | ||||||||||||||||||||||||||
getnanotime(&tfd->tfd_birthtim); | ||||||||||||||||||||||||||
LIST_INSERT_HEAD(&timerfd_head, tfd, entry); | ||||||||||||||||||||||||||
error = falloc(td, &fp, &fd, fflags); | ||||||||||||||||||||||||||
if (error != 0) | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
fflags = FREAD; | ||||||||||||||||||||||||||
Done Inline Actionsthis *does* fail and leaks tfd. the only thing which can fail in this func is falloc. just do it first. apart from missing timerfd_head locking mentioned by kib, I have not looked at other stuff mjg: this *does* fail and leaks tfd.
the only thing which can fail in this func is falloc. just do… | ||||||||||||||||||||||||||
Done Inline Actions
Thanks for the corrections. I'll add these changes in a new patch. jfree: > the only thing which can fail in this func is falloc. just do it first.
Thanks for the… | ||||||||||||||||||||||||||
if ((flags & TFD_NONBLOCK) != 0) | ||||||||||||||||||||||||||
fflags |= FNONBLOCK; | ||||||||||||||||||||||||||
finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops); | ||||||||||||||||||||||||||
fdrop(fp, td); | ||||||||||||||||||||||||||
Done Inline ActionsThe indent is wrong. kib: The indent is wrong. | ||||||||||||||||||||||||||
td->td_retval[0] = fd; | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct file *fp; | ||||||||||||||||||||||||||
struct timerfd *tfd; | ||||||||||||||||||||||||||
int error; | ||||||||||||||||||||||||||
error = fget(td, fd, &cap_write_rights, &fp); | ||||||||||||||||||||||||||
if (error != 0) | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
tfd = fp->f_data; | ||||||||||||||||||||||||||
if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { | ||||||||||||||||||||||||||
Done Inline ActionsPut {} around both cases in the if. They are multiline. kib: Put {} around both cases in the if. They are multiline. | ||||||||||||||||||||||||||
fdrop(fp, td); | ||||||||||||||||||||||||||
Done Inline ActionsSince TFD_SETTIME is IOWR args is a kernel pointer to a copy of the struct copied in by the ioctl framework. The fact that copyin works is an accident of the implementation. Likewise the copyout is redundant. brooks: Since TFD_SETTIME is IOWR `args` is a kernel pointer to a copy of the struct copied in by the… | ||||||||||||||||||||||||||
Done Inline ActionsI was under the impression that ioctl() copied in args, but not the pointers inside of args: args->new_value and args->old_value which are both struct itimerspec *. Does ioctl() recursively copyin? jfree: I was under the impression that ioctl() copied in `args`, but not the pointers inside of args… | ||||||||||||||||||||||||||
Done Inline ActionsSorry, not fully awake when I wrote that. You are correct. I had a different model in my head. brooks: Sorry, not fully awake when I wrote that. You are correct. I had a different model in my head. | ||||||||||||||||||||||||||
Done Inline ActionsFWIW, copyin/out from/to kernel addresses cause EFAULT. Not doing so results in the huge security hole. kib: FWIW, copyin/out from/to kernel addresses cause EFAULT. Not doing so results in the huge… | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
timerfd_curval(tfd, curr_value); | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
fdrop(fp, td); | ||||||||||||||||||||||||||
return (0); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
kern_timerfd_settime(struct thread *td, int fd, int flags, | ||||||||||||||||||||||||||
const struct itimerspec *new_value, struct itimerspec *old_value) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct file *fp; | ||||||||||||||||||||||||||
struct timerfd *tfd; | ||||||||||||||||||||||||||
struct timespec ts; | ||||||||||||||||||||||||||
Done Inline Actions
brooks: | ||||||||||||||||||||||||||
int error = 0; | ||||||||||||||||||||||||||
if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
if (!timespecvalid_interval(&new_value->it_value) || | ||||||||||||||||||||||||||
!timespecvalid_interval(&new_value->it_interval)) | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
error = fget(td, fd, &cap_write_rights, &fp); | ||||||||||||||||||||||||||
Done Inline Actions
struct itimerspec32 has 32-bits of padding on non-x86 platforms so needs to be zeroed or we'll leak that to userspace. Is the pold_value juggling worth doing? The cost of a few four dead stores to the stack doesn't seem worth avoiding in the args->old_value==0 case. brooks: `struct itimerspec32` has 32-bits of padding on non-x86 platforms so needs to be zeroed or… | ||||||||||||||||||||||||||
Done Inline Actions
We can skip the process of assigning the old time to (p)old_value if it is passed in as NULL. If not NULL, then timerfd_curval() will be called for every timerfd_settime_common(). I figured a single pointer stack allocation would be less resource intensive than calling curval involuntarily. jfree: > Is the pold_value juggling worth doing? The cost of a few four dead stores to the stack… | ||||||||||||||||||||||||||
if (error != 0) | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
tfd = fp->f_data; | ||||||||||||||||||||||||||
if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { | ||||||||||||||||||||||||||
fdrop(fp, td); | ||||||||||||||||||||||||||
Done Inline Actions
brooks: | ||||||||||||||||||||||||||
return (EINVAL); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
mtx_lock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
getnanotime(&tfd->tfd_mtim); | ||||||||||||||||||||||||||
tfd->tfd_timflags = flags; | ||||||||||||||||||||||||||
/* Store old itimerspec, if applicable. */ | ||||||||||||||||||||||||||
if (old_value != NULL) | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
timerfd_curval(tfd, old_value); | ||||||||||||||||||||||||||
Done Inline Actions
kib: | ||||||||||||||||||||||||||
/* Set new expiration. */ | ||||||||||||||||||||||||||
tfd->tfd_time = *new_value; | ||||||||||||||||||||||||||
if (timespecisset(&tfd->tfd_time.it_value)) { | ||||||||||||||||||||||||||
if ((flags & TFD_TIMER_ABSTIME) == 0) { | ||||||||||||||||||||||||||
nanouptime(&ts); | ||||||||||||||||||||||||||
timespecadd(&tfd->tfd_time.it_value, &ts, | ||||||||||||||||||||||||||
&tfd->tfd_time.it_value); | ||||||||||||||||||||||||||
} else if (tfd->tfd_clockid == CLOCK_REALTIME) { | ||||||||||||||||||||||||||
/* ECANCELED if unread jump is pending. */ | ||||||||||||||||||||||||||
if (tfd->tfd_jumped == TFD_CANCELED) | ||||||||||||||||||||||||||
error = ECANCELED; | ||||||||||||||||||||||||||
/* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */ | ||||||||||||||||||||||||||
timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim, | ||||||||||||||||||||||||||
&tfd->tfd_time.it_value); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
callout_reset_sbt(&tfd->tfd_callout, | ||||||||||||||||||||||||||
tstosbt(tfd->tfd_time.it_value), | ||||||||||||||||||||||||||
0, timerfd_expire, tfd, C_ABSOLUTE); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
callout_stop(&tfd->tfd_callout); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
tfd->tfd_count = 0; | ||||||||||||||||||||||||||
tfd->tfd_expired = false; | ||||||||||||||||||||||||||
tfd->tfd_jumped = TFD_NOJUMP; | ||||||||||||||||||||||||||
mtx_unlock(&tfd->tfd_lock); | ||||||||||||||||||||||||||
fdrop(fp, td); | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
return (kern_timerfd_create(td, uap->clockid, uap->flags)); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct itimerspec curr_value; | ||||||||||||||||||||||||||
int error; | ||||||||||||||||||||||||||
error = kern_timerfd_gettime(td, uap->fd, &curr_value); | ||||||||||||||||||||||||||
if (error == 0) | ||||||||||||||||||||||||||
error = copyout(&curr_value, uap->curr_value, | ||||||||||||||||||||||||||
sizeof(curr_value)); | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct itimerspec new_value, old_value; | ||||||||||||||||||||||||||
int error; | ||||||||||||||||||||||||||
error = copyin(uap->new_value, &new_value, sizeof(new_value)); | ||||||||||||||||||||||||||
if (error != 0) | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
if (uap->old_value == NULL) { | ||||||||||||||||||||||||||
error = kern_timerfd_settime(td, uap->fd, uap->flags, | ||||||||||||||||||||||||||
&new_value, NULL); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
error = kern_timerfd_settime(td, uap->fd, uap->flags, | ||||||||||||||||||||||||||
&new_value, &old_value); | ||||||||||||||||||||||||||
if (error == 0) | ||||||||||||||||||||||||||
error = copyout(&old_value, uap->old_value, | ||||||||||||||||||||||||||
sizeof(old_value)); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
#ifdef COMPAT_FREEBSD32 | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
freebsd32_timerfd_gettime(struct thread *td, | ||||||||||||||||||||||||||
struct freebsd32_timerfd_gettime_args *uap) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct itimerspec curr_value; | ||||||||||||||||||||||||||
struct itimerspec32 curr_value32; | ||||||||||||||||||||||||||
int error; | ||||||||||||||||||||||||||
error = kern_timerfd_gettime(td, uap->fd, &curr_value); | ||||||||||||||||||||||||||
if (error == 0) { | ||||||||||||||||||||||||||
CP(curr_value, curr_value32, it_value.tv_sec); | ||||||||||||||||||||||||||
CP(curr_value, curr_value32, it_value.tv_nsec); | ||||||||||||||||||||||||||
CP(curr_value, curr_value32, it_interval.tv_sec); | ||||||||||||||||||||||||||
CP(curr_value, curr_value32, it_interval.tv_nsec); | ||||||||||||||||||||||||||
error = copyout(&curr_value32, uap->curr_value, | ||||||||||||||||||||||||||
sizeof(curr_value32)); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
int | ||||||||||||||||||||||||||
freebsd32_timerfd_settime(struct thread *td, | ||||||||||||||||||||||||||
struct freebsd32_timerfd_settime_args *uap) | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
struct itimerspec new_value, old_value; | ||||||||||||||||||||||||||
struct itimerspec32 new_value32, old_value32; | ||||||||||||||||||||||||||
int error; | ||||||||||||||||||||||||||
error = copyin(uap->new_value, &new_value32, sizeof(new_value32)); | ||||||||||||||||||||||||||
if (error != 0) | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
CP(new_value32, new_value, it_value.tv_sec); | ||||||||||||||||||||||||||
CP(new_value32, new_value, it_value.tv_nsec); | ||||||||||||||||||||||||||
CP(new_value32, new_value, it_interval.tv_sec); | ||||||||||||||||||||||||||
CP(new_value32, new_value, it_interval.tv_nsec); | ||||||||||||||||||||||||||
if (uap->old_value == NULL) { | ||||||||||||||||||||||||||
error = kern_timerfd_settime(td, uap->fd, uap->flags, | ||||||||||||||||||||||||||
&new_value, NULL); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
error = kern_timerfd_settime(td, uap->fd, uap->flags, | ||||||||||||||||||||||||||
&new_value, &old_value); | ||||||||||||||||||||||||||
if (error == 0) { | ||||||||||||||||||||||||||
CP(old_value, old_value32, it_value.tv_sec); | ||||||||||||||||||||||||||
CP(old_value, old_value32, it_value.tv_nsec); | ||||||||||||||||||||||||||
CP(old_value, old_value32, it_interval.tv_sec); | ||||||||||||||||||||||||||
CP(old_value, old_value32, it_interval.tv_nsec); | ||||||||||||||||||||||||||
error = copyout(&old_value32, uap->old_value, | ||||||||||||||||||||||||||
sizeof(old_value32)); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
return (error); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
#endif |
should be removed, Roman was involved in the epoll part