xref: /netbsd-src/sys/kern/subr_time.c (revision 65195a4c92dbfea163b48af57736b67314fc5cbb)
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