xref: /illumos-gate/usr/src/uts/common/syscall/lwp_timer.c (revision d4660949aa62dd6a963f4913b7120b383cf473c4)
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 2008 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 	 * Requeue the timeout if no one has reset the system time
63 	 * and if the absolute future time has not been reached.
64 	 */
65 	if (lwptp->lwpt_timecheck == timechanged &&
66 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
67 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
68 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
69 		lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp,
70 		    timespectohz(&lwptp->lwpt_rqtime, now));
71 	} else {
72 		/*
73 		 * Set the thread running only if it is asleep on
74 		 * its lwpchan sleep queue (not if it is asleep on
75 		 * the t_delay_lock mutex).
76 		 */
77 		thread_lock(t);
78 		if (t->t_state == TS_SLEEP &&
79 		    (t->t_flag & T_WAKEABLE) &&
80 		    t->t_wchan0 != NULL)
81 			setrun_locked(t);
82 		thread_unlock(t);
83 	}
84 	mutex_exit(&t->t_delay_lock);
85 }
86 
87 int
88 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp)
89 {
90 	timespec_t now;
91 	int error = 0;
92 
93 	if (tsp == NULL)	/* not really an error, just need to bzero() */
94 		goto err;
95 	lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */
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 	}
131 	return (0);
132 err:
133 	bzero(lwptp, sizeof (lwp_timer_t));
134 	lwptp->lwpt_time_error = error;
135 	return (error);
136 }
137 
138 int
139 lwp_timer_enqueue(lwp_timer_t *lwptp)
140 {
141 	timespec_t now;
142 
143 	ASSERT(lwptp->lwpt_thread == curthread);
144 	ASSERT(MUTEX_HELD(&curthread->t_delay_lock));
145 	gethrestime(&now);
146 	if (lwptp->lwpt_timecheck == timechanged &&
147 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
148 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
149 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
150 		/*
151 		 * Queue the timeout.
152 		 */
153 		lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp,
154 		    timespectohz(&lwptp->lwpt_rqtime, now));
155 		return (0);
156 	}
157 
158 	/*
159 	 * Time has already run out or someone reset the system time;
160 	 * just cause an immediate timeout.
161 	 */
162 	lwptp->lwpt_imm_timeout = 1;
163 	return (1);
164 }
165 
166 clock_t
167 lwp_timer_dequeue(lwp_timer_t *lwptp)
168 {
169 	kthread_t *t = curthread;
170 	clock_t tim = -1;
171 	timeout_id_t tmp_id;
172 
173 	mutex_enter(&t->t_delay_lock);
174 	while ((tmp_id = lwptp->lwpt_id) != 0) {
175 		lwptp->lwpt_id = 0;
176 		mutex_exit(&t->t_delay_lock);
177 		tim = untimeout(tmp_id);
178 		mutex_enter(&t->t_delay_lock);
179 	}
180 	mutex_exit(&t->t_delay_lock);
181 	return (tim);
182 }
183 
184 int
185 lwp_timer_copyout(lwp_timer_t *lwptp, int error)
186 {
187 	timespec_t rmtime;
188 	timespec_t now;
189 
190 	if (lwptp->lwpt_tsp == NULL)	/* nothing to do */
191 		return (error);
192 
193 	rmtime.tv_sec = rmtime.tv_nsec = 0;
194 	if (error != ETIME) {
195 		gethrestime(&now);
196 		if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) ||
197 		    ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) &&
198 		    (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) {
199 			rmtime = lwptp->lwpt_rqtime;
200 			timespecsub(&rmtime, &now);
201 		}
202 	}
203 	if (curproc->p_model == DATAMODEL_NATIVE) {
204 		if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t)))
205 			error = EFAULT;
206 	} else {
207 		timespec32_t rmtime32;
208 
209 		TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
210 		if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t)))
211 			error = EFAULT;
212 	}
213 
214 	return (error);
215 }
216