/*-
* Copyright (c) 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* timerfd
*
* Timerfd objects are similar to POSIX timers, except they are associated
* with a file descriptor rather than a process. Timerfd objects are
* created with the timerfd_create(2) system call, similar to timer_create(2).
* The timerfd analogues for timer_gettime(2) and timer_settime(2) are
* timerfd_gettime(2) and timerfd_settime(2), respectively.
*
* When a timerfd object's timer fires, an internal counter is incremented.
* When this counter is non-zero, the descriptor associated with the timerfd
* object is "readable". Note that this is slightly different than the
* POSIX timer "overrun" counter, which only increments if the timer fires
* again while the notification signal is already pending. Thus, we are
* responsible for incrementing the "overrun" counter each time the timerfd
* timer fires.
*
* This implementation is API compatible with the Linux timerfd interface.
*/
/* N.B. all timerfd state is protected by itimer_lock() */
struct timerfd {
struct itimer tfd_itimer;
kcondvar_t tfd_read_wait;
struct selinfo tfd_read_sel;
int64_t tfd_nwaiters;
bool tfd_cancel_on_set;
bool tfd_cancelled;
bool tfd_restarting;
/*
* Information kept for stat(2).
*/
struct timespec tfd_btime; /* time created */
struct timespec tfd_mtime; /* last timerfd_settime() */
struct timespec tfd_atime; /* last read */
};
/*
* timerfd_wait:
*
* Block on a timerfd. Handles non-blocking, as well as
* the restart cases.
*/
static int
timerfd_wait(struct timerfd * const tfd, int const fflag)
{
extern kmutex_t itimer_mutex; /* XXX */
int error;
if (fflag & FNONBLOCK) {
return EAGAIN;
}
/*
* We're going to block. Check if we need to return ERESTART.
*/
if (tfd->tfd_restarting) {
return ERESTART;
}
/*
* If a restart was triggered while we were asleep, we need
* to return ERESTART if no other error was returned.
*/
if (tfd->tfd_restarting) {
if (error == 0) {
error = ERESTART;
}
}
return error;
}
/*
* timerfd_wake:
*
* Wake LWPs blocked on a timerfd.
*/
static void
timerfd_wake(struct timerfd * const tfd)
{
/*
* If we've been passed a relative value, convert it to an
* absolute, as that's what the itimer facility expects for
* non-virtual timers. Also ensure that this doesn't set it
* to zero or lets it go negative.
* XXXJRT re-factor.
*/
if (timespecisset(&it->it_time.it_value) &&
(flags & TFD_TIMER_ABSTIME) == 0) {
struct timespec now;
if (it->it_clockid == CLOCK_REALTIME) {
getnanotime(&now);
} else { /* CLOCK_MONOTONIC */
getnanouptime(&now);
}
timespecadd(&it->it_time.it_value, &now,
&it->it_time.it_value);
}