xref: /illumos-gate/usr/src/uts/common/syscall/lwp_timer.c (revision 87a18d3f5ba5da1985af86439cf1c94a9118b665)
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 #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
54 lwp_timer_timeout(void *arg)
55 {
56 	lwp_timer_t *lwptp = arg;
57 	kthread_t *t = lwptp->lwpt_thread;
58 	timespec_t now;
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 		lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME,
72 		    lwp_timer_timeout, lwptp,
73 		    TICK_TO_NSEC(timespectohz(&lwptp->lwpt_rqtime, now)),
74 		    nsec_per_tick, CALLOUT_FLAG_HRESTIME);
75 	} else {
76 		/*
77 		 * Set the thread running only if it is asleep on
78 		 * its lwpchan sleep queue (not if it is asleep on
79 		 * the t_delay_lock mutex).
80 		 */
81 		thread_lock(t);
82 		/* do this for the benefit of upi mutexes */
83 		(void) atomic_cas_uint(&lwptp->lwpt_imm_timeout, 0, 1);
84 		if (t->t_state == TS_SLEEP &&
85 		    (t->t_flag & T_WAKEABLE) &&
86 		    t->t_wchan0 != NULL)
87 			setrun_locked(t);
88 		thread_unlock(t);
89 	}
90 	mutex_exit(&t->t_delay_lock);
91 }
92 
93 int
94 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp)
95 {
96 	timespec_t now;
97 	int error = 0;
98 
99 	if (tsp == NULL)	/* not really an error, just need to bzero() */
100 		goto err;
101 	lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */
102 	gethrestime(&now);		/* do this before copyin() */
103 	if (curproc->p_model == DATAMODEL_NATIVE) {
104 		if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) {
105 			error = EFAULT;
106 			goto err;
107 		}
108 	} else {
109 		timespec32_t ts32;
110 		if (copyin(tsp, &ts32, sizeof (timespec32_t))) {
111 			error = EFAULT;
112 			goto err;
113 		}
114 		TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32);
115 	}
116 	if (itimerspecfix(&lwptp->lwpt_rqtime)) {
117 		error = EINVAL;
118 		goto err;
119 	}
120 	/*
121 	 * Unless the requested timeout is zero,
122 	 * get the precise future (absolute) time at
123 	 * which we are to time out and return ETIME.
124 	 * We must not return ETIME before that time.
125 	 */
126 	if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) {
127 		bzero(lwptp, sizeof (lwp_timer_t));
128 		lwptp->lwpt_imm_timeout = 1;
129 	} else {
130 		lwptp->lwpt_thread = curthread;
131 		lwptp->lwpt_tsp = tsp;
132 		lwptp->lwpt_time_error = 0;
133 		lwptp->lwpt_id = 0;
134 		lwptp->lwpt_imm_timeout = 0;
135 		timespecadd(&lwptp->lwpt_rqtime, &now);
136 	}
137 	return (0);
138 err:
139 	bzero(lwptp, sizeof (lwp_timer_t));
140 	lwptp->lwpt_time_error = error;
141 	return (error);
142 }
143 
144 int
145 lwp_timer_enqueue(lwp_timer_t *lwptp)
146 {
147 	timespec_t now;
148 
149 	ASSERT(lwptp->lwpt_thread == curthread);
150 	ASSERT(MUTEX_HELD(&curthread->t_delay_lock));
151 	gethrestime(&now);
152 	if (lwptp->lwpt_timecheck == timechanged &&
153 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
154 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
155 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
156 		/*
157 		 * Queue the timeout.
158 		 */
159 		lwptp->lwpt_imm_timeout = 0;
160 		lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME,
161 		    lwp_timer_timeout, lwptp,
162 		    TICK_TO_NSEC(timespectohz(&lwptp->lwpt_rqtime, now)),
163 		    nsec_per_tick, CALLOUT_FLAG_HRESTIME);
164 		return (0);
165 	}
166 
167 	/*
168 	 * Time has already run out or someone reset the system time;
169 	 * just cause an immediate timeout.
170 	 */
171 	lwptp->lwpt_imm_timeout = 1;
172 	return (1);
173 }
174 
175 clock_t
176 lwp_timer_dequeue(lwp_timer_t *lwptp)
177 {
178 	kthread_t *t = curthread;
179 	clock_t tim = -1;
180 	callout_id_t tmp_id;
181 
182 	mutex_enter(&t->t_delay_lock);
183 	while ((tmp_id = lwptp->lwpt_id) != 0) {
184 		lwptp->lwpt_id = 0;
185 		mutex_exit(&t->t_delay_lock);
186 		tim = untimeout_default(tmp_id, 0);
187 		mutex_enter(&t->t_delay_lock);
188 	}
189 	mutex_exit(&t->t_delay_lock);
190 	return (tim);
191 }
192 
193 int
194 lwp_timer_copyout(lwp_timer_t *lwptp, int error)
195 {
196 	timespec_t rmtime;
197 	timespec_t now;
198 
199 	if (lwptp->lwpt_tsp == NULL)	/* nothing to do */
200 		return (error);
201 
202 	rmtime.tv_sec = rmtime.tv_nsec = 0;
203 	if (error != ETIME) {
204 		gethrestime(&now);
205 		if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) ||
206 		    ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) &&
207 		    (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) {
208 			rmtime = lwptp->lwpt_rqtime;
209 			timespecsub(&rmtime, &now);
210 		}
211 	}
212 	if (curproc->p_model == DATAMODEL_NATIVE) {
213 		if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t)))
214 			error = EFAULT;
215 	} else {
216 		timespec32_t rmtime32;
217 
218 		TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
219 		if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t)))
220 			error = EFAULT;
221 	}
222 
223 	return (error);
224 }
225