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