1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/proc.h>
28 #include <sys/systm.h>
29 #include <sys/debug.h>
30 #include <sys/mutex.h>
31 #include <sys/atomic.h>
32 #include <sys/timer.h>
33 #include <sys/lwp_timer_impl.h>
34 #include <sys/callo.h>
35
36 /*
37 * lwp_timer_timeout() is called from a timeout set up in lwp_cond_wait(),
38 * lwp_mutex_timedlock(), lwp_sema_timedwait() or lwp_rwlock_lock().
39 *
40 * It recomputes the time remaining until the absolute time when the
41 * wait is supposed to timeout and either calls realtime_timeout()
42 * to reschedule itself or calls setrun() on the sleeping thread.
43 *
44 * This is done to ensure that the waiting thread does not wake up
45 * due to timer expiration until the absolute future time of the
46 * timeout has been reached. Until that time, the thread must
47 * remain on its sleep queue.
48 *
49 * An lwp_timer_t structure is used to pass information
50 * about the sleeping thread to the timeout function.
51 */
52
53 static void
lwp_timer_timeout(void * arg)54 lwp_timer_timeout(void *arg)
55 {
56 lwp_timer_t *lwptp = arg;
57 kthread_t *t = lwptp->lwpt_thread;
58 timespec_t now, delta;
59
60 mutex_enter(&t->t_delay_lock);
61 gethrestime(&now);
62 /*
63 * Requeue the timeout if no one has reset the system time
64 * and if the absolute future time has not been reached.
65 */
66 if (lwptp->lwpt_timecheck == timechanged &&
67 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
68 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
69 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
70 lwptp->lwpt_imm_timeout = 0;
71 delta = lwptp->lwpt_rqtime;
72 timespecsub(&delta, &now);
73 lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME,
74 lwp_timer_timeout, lwptp, ts2hrt(&delta), nsec_per_tick,
75 (CALLOUT_FLAG_HRESTIME | CALLOUT_FLAG_ROUNDUP));
76 } else {
77 /*
78 * Set the thread running only if it is asleep on
79 * its lwpchan sleep queue (not if it is asleep on
80 * the t_delay_lock mutex).
81 */
82 thread_lock(t);
83 /* do this for the benefit of upi mutexes */
84 (void) atomic_cas_uint(&lwptp->lwpt_imm_timeout, 0, 1);
85 if (t->t_state == TS_SLEEP &&
86 (t->t_flag & T_WAKEABLE) &&
87 t->t_wchan0 != NULL)
88 setrun_locked(t);
89 thread_unlock(t);
90 }
91 mutex_exit(&t->t_delay_lock);
92 }
93
94 int
lwp_timer_copyin(lwp_timer_t * lwptp,timespec_t * tsp)95 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp)
96 {
97 timespec_t now;
98 int error = 0;
99
100 if (tsp == NULL) /* not really an error, just need to bzero() */
101 goto err;
102 lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */
103 gethrestime(&now); /* do this before copyin() */
104 if (curproc->p_model == DATAMODEL_NATIVE) {
105 if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) {
106 error = EFAULT;
107 goto err;
108 }
109 } else {
110 timespec32_t ts32;
111 if (copyin(tsp, &ts32, sizeof (timespec32_t))) {
112 error = EFAULT;
113 goto err;
114 }
115 TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32);
116 }
117 if (itimerspecfix(&lwptp->lwpt_rqtime)) {
118 error = EINVAL;
119 goto err;
120 }
121 /*
122 * Unless the requested timeout is zero,
123 * get the precise future (absolute) time at
124 * which we are to time out and return ETIME.
125 * We must not return ETIME before that time.
126 */
127 if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) {
128 bzero(lwptp, sizeof (lwp_timer_t));
129 lwptp->lwpt_imm_timeout = 1;
130 } else {
131 lwptp->lwpt_thread = curthread;
132 lwptp->lwpt_tsp = tsp;
133 lwptp->lwpt_time_error = 0;
134 lwptp->lwpt_id = 0;
135 lwptp->lwpt_imm_timeout = 0;
136 timespecadd(&lwptp->lwpt_rqtime, &now);
137 }
138 return (0);
139 err:
140 bzero(lwptp, sizeof (lwp_timer_t));
141 lwptp->lwpt_time_error = error;
142 return (error);
143 }
144
145 int
lwp_timer_enqueue(lwp_timer_t * lwptp)146 lwp_timer_enqueue(lwp_timer_t *lwptp)
147 {
148 timespec_t now, delta;
149
150 ASSERT(lwptp->lwpt_thread == curthread);
151 ASSERT(MUTEX_HELD(&curthread->t_delay_lock));
152 gethrestime(&now);
153 if (lwptp->lwpt_timecheck == timechanged &&
154 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
155 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
156 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
157 /*
158 * Queue the timeout.
159 */
160 lwptp->lwpt_imm_timeout = 0;
161 delta = lwptp->lwpt_rqtime;
162 timespecsub(&delta, &now);
163 lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME,
164 lwp_timer_timeout, lwptp, ts2hrt(&delta), nsec_per_tick,
165 (CALLOUT_FLAG_HRESTIME | CALLOUT_FLAG_ROUNDUP));
166 return (0);
167 }
168
169 /*
170 * Time has already run out or someone reset the system time;
171 * just cause an immediate timeout.
172 */
173 lwptp->lwpt_imm_timeout = 1;
174 return (1);
175 }
176
177 clock_t
lwp_timer_dequeue(lwp_timer_t * lwptp)178 lwp_timer_dequeue(lwp_timer_t *lwptp)
179 {
180 kthread_t *t = curthread;
181 clock_t tim = -1;
182 callout_id_t tmp_id;
183
184 mutex_enter(&t->t_delay_lock);
185 while ((tmp_id = lwptp->lwpt_id) != 0) {
186 lwptp->lwpt_id = 0;
187 mutex_exit(&t->t_delay_lock);
188 tim = untimeout_default(tmp_id, 0);
189 mutex_enter(&t->t_delay_lock);
190 }
191 mutex_exit(&t->t_delay_lock);
192 return (tim);
193 }
194
195 int
lwp_timer_copyout(lwp_timer_t * lwptp,int error)196 lwp_timer_copyout(lwp_timer_t *lwptp, int error)
197 {
198 timespec_t rmtime;
199 timespec_t now;
200
201 if (lwptp->lwpt_tsp == NULL) /* nothing to do */
202 return (error);
203
204 rmtime.tv_sec = rmtime.tv_nsec = 0;
205 if (error != ETIME) {
206 gethrestime(&now);
207 if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) ||
208 ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) &&
209 (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) {
210 rmtime = lwptp->lwpt_rqtime;
211 timespecsub(&rmtime, &now);
212 }
213 }
214 if (curproc->p_model == DATAMODEL_NATIVE) {
215 if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t)))
216 error = EFAULT;
217 } else {
218 timespec32_t rmtime32;
219
220 TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
221 if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t)))
222 error = EFAULT;
223 }
224
225 return (error);
226 }
227