xref: /illumos-gate/usr/src/uts/common/os/clock_thread.c (revision dea9f5e6a4938723acec9624b3aa3f680f2f5c9f)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2021 Oxide Computer Company
14  */
15 
16 /*
17  * This clock backend implements basic support for the following two clocks:
18  *
19  *   o CLOCK_VIRTUAL		This provides the ability to read the amount of
20  *				user CPU time that the calling thread has spent
21  *				on CPU. This is the LMS_USER cpu microstate.
22  *
23  *   o CLOCK_THREAD_CPUTIME_ID	This clock is similar to the above; however, it
24  *				also includes system time. This is the LMS_USER,
25  *				LMS_SYSTEM, and LMS_TRAP microstates combined
26  *				together. We include LMS_TRAP here because that
27  *				is what you see in a thread's lwpstatus file.
28  *
29  * At this time, we only provide the ability to read the current time (e.g.
30  * through a call to clock_gettime(3C)). There is never a case where being able
31  * to set the time makes sense today and truthfully, lying about a process's
32  * runtime should be left to mdb -kw. Today, we do not support the ability to
33  * create interval timers based on this backend (e.g. timer_create(3C) and
34  * timer_settime(3C)). However, there is no reason that couldn't be added.
35  *
36  * A nice simplification here is that this clock is always about reading from
37  * the current thread. This means that one can always access it. Because the
38  * calling thread exists and is in this code, it means that we know it is here.
39  * Any other privilege information is left to the broader kernel.
40  *
41  * Because the only difference between these is the question of whether or not
42  * we include LMS_SYSTEM time in the value, we generally use the same actual
43  * clock backend functions except for the one that implements
44  * clk_clock_gettime().
45  */
46 
47 #include <sys/timer.h>
48 #include <sys/cyclic.h>
49 #include <sys/msacct.h>
50 
51 static clock_backend_t clock_thread_usr;
52 static clock_backend_t clock_thread_usrsys;
53 
54 static int
clock_thread_settime(timespec_t * ts)55 clock_thread_settime(timespec_t *ts)
56 {
57 	return (EINVAL);
58 }
59 
60 static int
clock_thread_usr_gettime(timespec_t * ts)61 clock_thread_usr_gettime(timespec_t *ts)
62 {
63 	hrtime_t hrt;
64 	kthread_t *t = curthread;
65 	klwp_t *lwp = ttolwp(t);
66 
67 	hrt = lwp->lwp_mstate.ms_acct[LMS_USER];
68 	scalehrtime(&hrt);
69 	hrt2ts(hrt, ts);
70 
71 	return (0);
72 }
73 
74 static int
clock_thread_usrsys_gettime(timespec_t * ts)75 clock_thread_usrsys_gettime(timespec_t *ts)
76 {
77 	hrtime_t hrt;
78 	kthread_t *t = curthread;
79 
80 	/*
81 	 * mstate_thread_onproc_time() takes care of doing the following:
82 	 *
83 	 *  o Combining LMS_USER, LMS_SYSTEM, and LMS_TRAP.
84 	 *  o Ensuring that the result is scaled
85 	 *  o Ensuring that the time that's elapsed to the point of our asking
86 	 *    is included. By definition the kernel is executing in LMS_SYSTEM
87 	 *    so this ensures that we add that time which isn't currently in the
88 	 *    microstate to this.
89 	 */
90 	thread_lock(t);
91 	hrt = mstate_thread_onproc_time(t);
92 	thread_unlock(t);
93 
94 	hrt2ts(hrt, ts);
95 	return (0);
96 }
97 
98 /*
99  * The question of the resolution here is a thorny one. Technically this would
100  * really be based upon the resolution of gethrtime_unscaled(), as we can
101  * actually tell that much due to our use of CPU microstate accounting. However,
102  * from a timer resolution perspective it's actually quite different and would
103  * in theory be based on the system tick rate.
104  *
105  * This basically leaves us with two options:
106  *
107  *   1) Use 'nsec_per_tick' to go down the Hz path.
108  *   2) Use the cyclic resolution, which basically is kind of the resolution of
109  *      that timer.
110  *
111  * POSIX is unclear as to the effect of the resolution in the case of timer_*()
112  * functions and only really says it is used to impact the implementation of
113  * clock_settime() which of course isn't actually supported here. As a result,
114  * we opt to prefer the cyclic resolution, which is closer to the actual
115  * resolution of this subsystem. Strictly speaking, this might not be completely
116  * accurate, but should be on current platforms.
117  */
118 static int
clock_thread_getres(timespec_t * ts)119 clock_thread_getres(timespec_t *ts)
120 {
121 	hrt2ts(cyclic_getres(), (timestruc_t *)ts);
122 
123 	return (0);
124 }
125 
126 static int
clock_thread_timer_create(itimer_t * it,void (* fire)(itimer_t *))127 clock_thread_timer_create(itimer_t *it, void (*fire)(itimer_t *))
128 {
129 	return (EINVAL);
130 }
131 
132 static int
clock_thread_timer_settime(itimer_t * it,int flags,const struct itimerspec * when)133 clock_thread_timer_settime(itimer_t *it, int flags,
134     const struct itimerspec *when)
135 {
136 	return (EINVAL);
137 }
138 
139 static int
clock_thread_timer_gettime(itimer_t * it,struct itimerspec * when)140 clock_thread_timer_gettime(itimer_t *it, struct itimerspec *when)
141 {
142 	return (EINVAL);
143 }
144 
145 static int
clock_thread_timer_delete(itimer_t * it)146 clock_thread_timer_delete(itimer_t *it)
147 {
148 	return (EINVAL);
149 }
150 
151 static void
clock_thread_timer_lwpbind(itimer_t * it)152 clock_thread_timer_lwpbind(itimer_t *it)
153 {
154 }
155 
156 void
clock_thread_init(void)157 clock_thread_init(void)
158 {
159 	/*
160 	 * While this clock backends don't support notifications right now, we
161 	 * still fill out the default for what it would be.
162 	 */
163 	clock_thread_usr.clk_default.sigev_signo = SIGALRM;
164 	clock_thread_usr.clk_default.sigev_notify = SIGEV_SIGNAL;
165 	clock_thread_usr.clk_default.sigev_value.sival_ptr = NULL;
166 
167 	clock_thread_usr.clk_clock_settime = clock_thread_settime;
168 	clock_thread_usr.clk_clock_gettime = clock_thread_usr_gettime;
169 	clock_thread_usr.clk_clock_getres = clock_thread_getres;
170 	clock_thread_usr.clk_timer_create = clock_thread_timer_create;
171 	clock_thread_usr.clk_timer_settime = clock_thread_timer_settime;
172 	clock_thread_usr.clk_timer_gettime = clock_thread_timer_gettime;
173 	clock_thread_usr.clk_timer_delete = clock_thread_timer_delete;
174 	clock_thread_usr.clk_timer_lwpbind = clock_thread_timer_lwpbind;
175 
176 	clock_thread_usrsys.clk_default.sigev_signo = SIGALRM;
177 	clock_thread_usrsys.clk_default.sigev_notify = SIGEV_SIGNAL;
178 	clock_thread_usrsys.clk_default.sigev_value.sival_ptr = NULL;
179 
180 	clock_thread_usrsys.clk_clock_settime = clock_thread_settime;
181 	clock_thread_usrsys.clk_clock_gettime = clock_thread_usrsys_gettime;
182 	clock_thread_usrsys.clk_clock_getres = clock_thread_getres;
183 	clock_thread_usrsys.clk_timer_create = clock_thread_timer_create;
184 	clock_thread_usrsys.clk_timer_settime = clock_thread_timer_settime;
185 	clock_thread_usrsys.clk_timer_gettime = clock_thread_timer_gettime;
186 	clock_thread_usrsys.clk_timer_delete = clock_thread_timer_delete;
187 	clock_thread_usrsys.clk_timer_lwpbind = clock_thread_timer_lwpbind;
188 
189 	clock_add_backend(CLOCK_VIRTUAL, &clock_thread_usr);
190 	clock_add_backend(CLOCK_THREAD_CPUTIME_ID, &clock_thread_usrsys);
191 }
192