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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/proc.h>
30 #include <sys/systm.h>
31 #include <sys/debug.h>
32 #include <sys/mutex.h>
33 #include <sys/atomic.h>
34 #include <sys/timer.h>
35 #include <sys/lwp_timer_impl.h>
36 
37 /*
38  * lwp_timer_timeout() is called from a timeout set up in lwp_cond_wait(),
39  * lwp_mutex_timedlock(), lwp_sema_timedwait() or lwp_rwlock_lock().
40  *
41  * It recomputes the time remaining until the absolute time when the
42  * wait is supposed to timeout and either calls realtime_timeout()
43  * to reschedule itself or calls setrun() on the sleeping thread.
44  *
45  * This is done to ensure that the waiting thread does not wake up
46  * due to timer expiration until the absolute future time of the
47  * timeout has been reached.  Until that time, the thread must
48  * remain on its sleep queue.
49  *
50  * An lwp_timer_t structure is used to pass information
51  * about the sleeping thread to the timeout function.
52  */
53 
54 static void
55 lwp_timer_timeout(void *arg)
56 {
57 	lwp_timer_t *lwptp = arg;
58 	kthread_t *t = lwptp->lwpt_thread;
59 	timespec_t now;
60 
61 	mutex_enter(&t->t_delay_lock);
62 	gethrestime(&now);
63 	/*
64 	 * Requeue the timeout if no one has reset the system time
65 	 * and if the absolute future time has not been reached.
66 	 */
67 	if (lwptp->lwpt_timecheck == timechanged &&
68 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
69 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
70 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
71 		lwptp->lwpt_imm_timeout = 0;
72 		lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp,
73 		    timespectohz(&lwptp->lwpt_rqtime, now));
74 	} else {
75 		/*
76 		 * Set the thread running only if it is asleep on
77 		 * its lwpchan sleep queue (not if it is asleep on
78 		 * the t_delay_lock mutex).
79 		 */
80 		thread_lock(t);
81 		/* do this for the benefit of upi mutexes */
82 		(void) atomic_cas_uint(&lwptp->lwpt_imm_timeout, 0, 1);
83 		if (t->t_state == TS_SLEEP &&
84 		    (t->t_flag & T_WAKEABLE) &&
85 		    t->t_wchan0 != NULL)
86 			setrun_locked(t);
87 		thread_unlock(t);
88 	}
89 	mutex_exit(&t->t_delay_lock);
90 }
91 
92 int
93 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp)
94 {
95 	timespec_t now;
96 	int error = 0;
97 
98 	if (tsp == NULL)	/* not really an error, just need to bzero() */
99 		goto err;
100 	lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */
101 	gethrestime(&now);		/* do this before copyin() */
102 	if (curproc->p_model == DATAMODEL_NATIVE) {
103 		if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) {
104 			error = EFAULT;
105 			goto err;
106 		}
107 	} else {
108 		timespec32_t ts32;
109 		if (copyin(tsp, &ts32, sizeof (timespec32_t))) {
110 			error = EFAULT;
111 			goto err;
112 		}
113 		TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32);
114 	}
115 	if (itimerspecfix(&lwptp->lwpt_rqtime)) {
116 		error = EINVAL;
117 		goto err;
118 	}
119 	/*
120 	 * Unless the requested timeout is zero,
121 	 * get the precise future (absolute) time at
122 	 * which we are to time out and return ETIME.
123 	 * We must not return ETIME before that time.
124 	 */
125 	if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) {
126 		bzero(lwptp, sizeof (lwp_timer_t));
127 		lwptp->lwpt_imm_timeout = 1;
128 	} else {
129 		lwptp->lwpt_thread = curthread;
130 		lwptp->lwpt_tsp = tsp;
131 		lwptp->lwpt_time_error = 0;
132 		lwptp->lwpt_id = 0;
133 		lwptp->lwpt_imm_timeout = 0;
134 		timespecadd(&lwptp->lwpt_rqtime, &now);
135 	}
136 	return (0);
137 err:
138 	bzero(lwptp, sizeof (lwp_timer_t));
139 	lwptp->lwpt_time_error = error;
140 	return (error);
141 }
142 
143 int
144 lwp_timer_enqueue(lwp_timer_t *lwptp)
145 {
146 	timespec_t now;
147 
148 	ASSERT(lwptp->lwpt_thread == curthread);
149 	ASSERT(MUTEX_HELD(&curthread->t_delay_lock));
150 	gethrestime(&now);
151 	if (lwptp->lwpt_timecheck == timechanged &&
152 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
153 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
154 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
155 		/*
156 		 * Queue the timeout.
157 		 */
158 		lwptp->lwpt_imm_timeout = 0;
159 		lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp,
160 		    timespectohz(&lwptp->lwpt_rqtime, now));
161 		return (0);
162 	}
163 
164 	/*
165 	 * Time has already run out or someone reset the system time;
166 	 * just cause an immediate timeout.
167 	 */
168 	lwptp->lwpt_imm_timeout = 1;
169 	return (1);
170 }
171 
172 clock_t
173 lwp_timer_dequeue(lwp_timer_t *lwptp)
174 {
175 	kthread_t *t = curthread;
176 	clock_t tim = -1;
177 	timeout_id_t tmp_id;
178 
179 	mutex_enter(&t->t_delay_lock);
180 	while ((tmp_id = lwptp->lwpt_id) != 0) {
181 		lwptp->lwpt_id = 0;
182 		mutex_exit(&t->t_delay_lock);
183 		tim = untimeout(tmp_id);
184 		mutex_enter(&t->t_delay_lock);
185 	}
186 	mutex_exit(&t->t_delay_lock);
187 	return (tim);
188 }
189 
190 int
191 lwp_timer_copyout(lwp_timer_t *lwptp, int error)
192 {
193 	timespec_t rmtime;
194 	timespec_t now;
195 
196 	if (lwptp->lwpt_tsp == NULL)	/* nothing to do */
197 		return (error);
198 
199 	rmtime.tv_sec = rmtime.tv_nsec = 0;
200 	if (error != ETIME) {
201 		gethrestime(&now);
202 		if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) ||
203 		    ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) &&
204 		    (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) {
205 			rmtime = lwptp->lwpt_rqtime;
206 			timespecsub(&rmtime, &now);
207 		}
208 	}
209 	if (curproc->p_model == DATAMODEL_NATIVE) {
210 		if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t)))
211 			error = EFAULT;
212 	} else {
213 		timespec32_t rmtime32;
214 
215 		TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
216 		if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t)))
217 			error = EFAULT;
218 	}
219 
220 	return (error);
221 }
222