subr_time.c revision 1.41 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
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
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
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
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
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
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
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
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
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