xref: /illumos-gate/usr/src/uts/common/syscall/lwp_timer.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 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/timer.h>
34 #include <sys/lwp_timer_impl.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_id = realtime_timeout(lwp_timer_timeout, lwptp,
71 			timespectohz_adj(&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 	lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */
97 	gethrestime(&now);		/* do this before copyin() */
98 	if (curproc->p_model == DATAMODEL_NATIVE) {
99 		if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) {
100 			error = EFAULT;
101 			goto err;
102 		}
103 	} else {
104 		timespec32_t ts32;
105 		if (copyin(tsp, &ts32, sizeof (timespec32_t))) {
106 			error = EFAULT;
107 			goto err;
108 		}
109 		TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32);
110 	}
111 	if (itimerspecfix(&lwptp->lwpt_rqtime)) {
112 		error = EINVAL;
113 		goto err;
114 	}
115 	/*
116 	 * Unless the requested timeout is zero,
117 	 * get the precise future (absolute) time at
118 	 * which we are to time out and return ETIME.
119 	 * We must not return ETIME before that time.
120 	 */
121 	if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) {
122 		bzero(lwptp, sizeof (lwp_timer_t));
123 		lwptp->lwpt_imm_timeout = 1;
124 	} else {
125 		lwptp->lwpt_thread = curthread;
126 		lwptp->lwpt_tsp = tsp;
127 		lwptp->lwpt_time_error = 0;
128 		lwptp->lwpt_id = 0;
129 		lwptp->lwpt_imm_timeout = 0;
130 		timespecadd(&lwptp->lwpt_rqtime, &now);
131 	}
132 	return (0);
133 err:
134 	bzero(lwptp, sizeof (lwp_timer_t));
135 	lwptp->lwpt_time_error = error;
136 	return (error);
137 }
138 
139 int
140 lwp_timer_enqueue(lwp_timer_t *lwptp)
141 {
142 	timespec_t now;
143 
144 	ASSERT(lwptp->lwpt_thread == curthread);
145 	ASSERT(MUTEX_HELD(&curthread->t_delay_lock));
146 	gethrestime(&now);
147 	if (lwptp->lwpt_timecheck == timechanged &&
148 	    (lwptp->lwpt_rqtime.tv_sec > now.tv_sec ||
149 	    (lwptp->lwpt_rqtime.tv_sec == now.tv_sec &&
150 	    lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) {
151 		/*
152 		 * Queue the timeout.
153 		 */
154 		lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp,
155 			timespectohz_adj(&lwptp->lwpt_rqtime, now));
156 		return (0);
157 	}
158 
159 	/*
160 	 * Time has already run out or someone reset the system time;
161 	 * just cause an immediate timeout.
162 	 */
163 	lwptp->lwpt_imm_timeout = 1;
164 	return (1);
165 }
166 
167 clock_t
168 lwp_timer_dequeue(lwp_timer_t *lwptp)
169 {
170 	kthread_t *t = curthread;
171 	clock_t tim = -1;
172 	timeout_id_t tmp_id;
173 
174 	mutex_enter(&t->t_delay_lock);
175 	while ((tmp_id = lwptp->lwpt_id) != 0) {
176 		lwptp->lwpt_id = 0;
177 		mutex_exit(&t->t_delay_lock);
178 		tim = untimeout(tmp_id);
179 		mutex_enter(&t->t_delay_lock);
180 	}
181 	mutex_exit(&t->t_delay_lock);
182 	return (tim);
183 }
184 
185 int
186 lwp_timer_copyout(lwp_timer_t *lwptp, int error)
187 {
188 	timespec_t rmtime;
189 	timespec_t now;
190 
191 	if (lwptp->lwpt_tsp == NULL)	/* nothing to do */
192 		return (error);
193 
194 	rmtime.tv_sec = rmtime.tv_nsec = 0;
195 	if (error != ETIME) {
196 		gethrestime(&now);
197 		if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) ||
198 		    ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) &&
199 		    (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) {
200 			rmtime = lwptp->lwpt_rqtime;
201 			timespecsub(&rmtime, &now);
202 		}
203 	}
204 	if (curproc->p_model == DATAMODEL_NATIVE) {
205 		if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t)))
206 			error = EFAULT;
207 	} else {
208 		timespec32_t rmtime32;
209 
210 		TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
211 		if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t)))
212 			error = EFAULT;
213 	}
214 
215 	return (error);
216 }
217