1 /*        $NetBSD: subr_time.c,v 1.41 2024/12/22 23:24:20 riastradh Exp $       */
2 
3 /*
4  * Copyright (c) 1982, 1986, 1989, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *        @(#)kern_clock.c    8.5 (Berkeley) 1/21/94
32  *        @(#)kern_time.c 8.4 (Berkeley) 5/26/95
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: subr_time.c,v 1.41 2024/12/22 23:24:20 riastradh Exp $");
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 
41 #include <sys/intr.h>
42 #include <sys/kauth.h>
43 #include <sys/kernel.h>
44 #include <sys/lwp.h>
45 #include <sys/proc.h>
46 #include <sys/time.h>
47 #include <sys/timetc.h>
48 #include <sys/timex.h>
49 
50 /*
51  * Compute number of hz until specified time.  Used to compute second
52  * argument to callout_reset() from an absolute time.
53  */
54 int
tvhzto(const struct timeval * tvp)55 tvhzto(const struct timeval *tvp)
56 {
57           struct timeval now, tv;
58 
59           tv = *tvp;          /* Don't modify original tvp. */
60           getmicrotime(&now);
61           timersub(&tv, &now, &tv);
62           return tvtohz(&tv);
63 }
64 
65 int
tshzto(const struct timespec * tsp)66 tshzto(const struct timespec *tsp)
67 {
68           struct timespec now, ts;
69 
70           ts = *tsp;          /* Don't modify original tsp. */
71           getnanotime(&now);
72           timespecsub(&ts, &now, &ts);
73           return tstohz(&ts);
74 }
75 
76 int
tshztoup(const struct timespec * tsp)77 tshztoup(const struct timespec *tsp)
78 {
79           struct timespec now, ts;
80 
81           ts = *tsp;          /* Don't modify original tsp. */
82           getnanouptime(&now);
83           timespecsub(&ts, &now, &ts);
84           return tstohz(&ts);
85 }
86 
87 /*
88  * Compute number of ticks in the specified amount of time.
89  */
90 int
tstohz(const struct timespec * ts)91 tstohz(const struct timespec *ts)
92 {
93           struct timeval tv;
94 
95           /*
96            * usec has great enough resolution for hz, so convert to a
97            * timeval and use tvtohz() above.
98            */
99           TIMESPEC_TO_TIMEVAL(&tv, ts);
100           return tvtohz(&tv);
101 }
102 
103 int
inittimeleft(struct timespec * ts,struct timespec * sleepts)104 inittimeleft(struct timespec *ts, struct timespec *sleepts)
105 {
106 
107           if (itimespecfix(ts)) {
108                     return -1;
109           }
110           KASSERT(ts->tv_sec >= 0);
111           getnanouptime(sleepts);
112           return 0;
113 }
114 
115 int
gettimeleft(struct timespec * ts,struct timespec * sleepts)116 gettimeleft(struct timespec *ts, struct timespec *sleepts)
117 {
118           struct timespec now, sleptts;
119 
120           KASSERT(ts->tv_sec >= 0);
121 
122           /*
123            * Reduce ts by elapsed time based on monotonic time scale.
124            */
125           getnanouptime(&now);
126           KASSERT(timespeccmp(sleepts, &now, <=));
127           timespecsub(&now, sleepts, &sleptts);
128           *sleepts = now;
129 
130           if (timespeccmp(ts, &sleptts, <=)) { /* timed out */
131                     timespecclear(ts);
132                     return 0;
133           }
134           timespecsub(ts, &sleptts, ts);
135 
136           return tstohz(ts);
137 }
138 
139 void
clock_timeleft(clockid_t clockid,struct timespec * ts,struct timespec * sleepts)140 clock_timeleft(clockid_t clockid, struct timespec *ts, struct timespec *sleepts)
141 {
142           struct timespec sleptts;
143 
144           clock_gettime1(clockid, &sleptts);
145           timespecadd(ts, sleepts, ts);
146           timespecsub(ts, &sleptts, ts);
147           *sleepts = sleptts;
148 }
149 
150 int
clock_gettime1(clockid_t clock_id,struct timespec * ts)151 clock_gettime1(clockid_t clock_id, struct timespec *ts)
152 {
153           int error;
154           struct proc *p;
155 
156 #define CPUCLOCK_ID_MASK (~(CLOCK_THREAD_CPUTIME_ID|CLOCK_PROCESS_CPUTIME_ID))
157           if (clock_id & CLOCK_PROCESS_CPUTIME_ID) {
158                     pid_t pid = clock_id & CPUCLOCK_ID_MASK;
159                     struct timeval cputime;
160 
161                     mutex_enter(&proc_lock);
162                     p = pid == 0 ? curproc : proc_find(pid);
163                     if (p == NULL) {
164                               mutex_exit(&proc_lock);
165                               return ESRCH;
166                     }
167                     mutex_enter(p->p_lock);
168                     calcru(p, /*usertime*/NULL, /*systime*/NULL, /*intrtime*/NULL,
169                         &cputime);
170                     mutex_exit(p->p_lock);
171                     mutex_exit(&proc_lock);
172 
173                     // XXX: Perhaps create a special kauth type
174                     error = kauth_authorize_process(kauth_cred_get(),
175                         KAUTH_PROCESS_PTRACE, p,
176                         KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL);
177                     if (error)
178                               return error;
179 
180                     TIMEVAL_TO_TIMESPEC(&cputime, ts);
181                     return 0;
182           } else if (clock_id & CLOCK_THREAD_CPUTIME_ID) {
183                     struct lwp *l;
184                     lwpid_t lid = clock_id & CPUCLOCK_ID_MASK;
185                     struct bintime tm = {0, 0};
186 
187                     p = curproc;
188                     mutex_enter(p->p_lock);
189                     l = lid == 0 ? curlwp : lwp_find(p, lid);
190                     if (l == NULL) {
191                               mutex_exit(p->p_lock);
192                               return ESRCH;
193                     }
194                     addrulwp(l, &tm);
195                     mutex_exit(p->p_lock);
196 
197                     bintime2timespec(&tm, ts);
198                     return 0;
199           }
200 
201           switch (clock_id) {
202           case CLOCK_REALTIME:
203                     nanotime(ts);
204                     break;
205           case CLOCK_MONOTONIC:
206                     nanouptime(ts);
207                     break;
208           default:
209                     return EINVAL;
210           }
211 
212           return 0;
213 }
214 
215 /*
216  * Calculate delta and convert from struct timespec to the ticks.
217  */
218 int
ts2timo(clockid_t clock_id,int flags,struct timespec * ts,int * timo,struct timespec * start)219 ts2timo(clockid_t clock_id, int flags, struct timespec *ts,
220     int *timo, struct timespec *start)
221 {
222           int error;
223           struct timespec tsd;
224 
225           if (ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000L)
226                     return EINVAL;
227 
228           if ((flags & TIMER_ABSTIME) != 0 || start != NULL) {
229                     error = clock_gettime1(clock_id, &tsd);
230                     if (error != 0)
231                               return error;
232                     if (start != NULL)
233                               *start = tsd;
234           }
235 
236           if ((flags & TIMER_ABSTIME) != 0) {
237                     if (!timespecsubok(ts, &tsd))
238                               return EINVAL;
239                     timespecsub(ts, &tsd, &tsd);
240                     ts = &tsd;
241           }
242 
243           error = itimespecfix(ts);
244           if (error != 0)
245                     return error;
246 
247           if (ts->tv_sec == 0 && ts->tv_nsec == 0)
248                     return ETIMEDOUT;
249 
250           *timo = tstohz(ts);
251           KASSERT(*timo > 0);
252 
253           return 0;
254 }
255