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