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