xref: /illumos-gate/usr/src/uts/common/syscall/lwp_timer.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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
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
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
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
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
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