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 #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/atomic.h> 34 #include <sys/timer.h> 35 #include <sys/lwp_timer_impl.h> 36 37 /* 38 * lwp_timer_timeout() is called from a timeout set up in lwp_cond_wait(), 39 * lwp_mutex_timedlock(), lwp_sema_timedwait() or lwp_rwlock_lock(). 40 * 41 * It recomputes the time remaining until the absolute time when the 42 * wait is supposed to timeout and either calls realtime_timeout() 43 * to reschedule itself or calls setrun() on the sleeping thread. 44 * 45 * This is done to ensure that the waiting thread does not wake up 46 * due to timer expiration until the absolute future time of the 47 * timeout has been reached. Until that time, the thread must 48 * remain on its sleep queue. 49 * 50 * An lwp_timer_t structure is used to pass information 51 * about the sleeping thread to the timeout function. 52 */ 53 54 static void 55 lwp_timer_timeout(void *arg) 56 { 57 lwp_timer_t *lwptp = arg; 58 kthread_t *t = lwptp->lwpt_thread; 59 timespec_t now; 60 61 mutex_enter(&t->t_delay_lock); 62 gethrestime(&now); 63 /* 64 * Requeue the timeout if no one has reset the system time 65 * and if the absolute future time has not been reached. 66 */ 67 if (lwptp->lwpt_timecheck == timechanged && 68 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec || 69 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec && 70 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) { 71 lwptp->lwpt_imm_timeout = 0; 72 lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp, 73 timespectohz(&lwptp->lwpt_rqtime, now)); 74 } else { 75 /* 76 * Set the thread running only if it is asleep on 77 * its lwpchan sleep queue (not if it is asleep on 78 * the t_delay_lock mutex). 79 */ 80 thread_lock(t); 81 /* do this for the benefit of upi mutexes */ 82 (void) atomic_cas_uint(&lwptp->lwpt_imm_timeout, 0, 1); 83 if (t->t_state == TS_SLEEP && 84 (t->t_flag & T_WAKEABLE) && 85 t->t_wchan0 != NULL) 86 setrun_locked(t); 87 thread_unlock(t); 88 } 89 mutex_exit(&t->t_delay_lock); 90 } 91 92 int 93 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp) 94 { 95 timespec_t now; 96 int error = 0; 97 98 if (tsp == NULL) /* not really an error, just need to bzero() */ 99 goto err; 100 lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */ 101 gethrestime(&now); /* do this before copyin() */ 102 if (curproc->p_model == DATAMODEL_NATIVE) { 103 if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) { 104 error = EFAULT; 105 goto err; 106 } 107 } else { 108 timespec32_t ts32; 109 if (copyin(tsp, &ts32, sizeof (timespec32_t))) { 110 error = EFAULT; 111 goto err; 112 } 113 TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32); 114 } 115 if (itimerspecfix(&lwptp->lwpt_rqtime)) { 116 error = EINVAL; 117 goto err; 118 } 119 /* 120 * Unless the requested timeout is zero, 121 * get the precise future (absolute) time at 122 * which we are to time out and return ETIME. 123 * We must not return ETIME before that time. 124 */ 125 if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) { 126 bzero(lwptp, sizeof (lwp_timer_t)); 127 lwptp->lwpt_imm_timeout = 1; 128 } else { 129 lwptp->lwpt_thread = curthread; 130 lwptp->lwpt_tsp = tsp; 131 lwptp->lwpt_time_error = 0; 132 lwptp->lwpt_id = 0; 133 lwptp->lwpt_imm_timeout = 0; 134 timespecadd(&lwptp->lwpt_rqtime, &now); 135 } 136 return (0); 137 err: 138 bzero(lwptp, sizeof (lwp_timer_t)); 139 lwptp->lwpt_time_error = error; 140 return (error); 141 } 142 143 int 144 lwp_timer_enqueue(lwp_timer_t *lwptp) 145 { 146 timespec_t now; 147 148 ASSERT(lwptp->lwpt_thread == curthread); 149 ASSERT(MUTEX_HELD(&curthread->t_delay_lock)); 150 gethrestime(&now); 151 if (lwptp->lwpt_timecheck == timechanged && 152 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec || 153 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec && 154 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) { 155 /* 156 * Queue the timeout. 157 */ 158 lwptp->lwpt_imm_timeout = 0; 159 lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp, 160 timespectohz(&lwptp->lwpt_rqtime, now)); 161 return (0); 162 } 163 164 /* 165 * Time has already run out or someone reset the system time; 166 * just cause an immediate timeout. 167 */ 168 lwptp->lwpt_imm_timeout = 1; 169 return (1); 170 } 171 172 clock_t 173 lwp_timer_dequeue(lwp_timer_t *lwptp) 174 { 175 kthread_t *t = curthread; 176 clock_t tim = -1; 177 timeout_id_t tmp_id; 178 179 mutex_enter(&t->t_delay_lock); 180 while ((tmp_id = lwptp->lwpt_id) != 0) { 181 lwptp->lwpt_id = 0; 182 mutex_exit(&t->t_delay_lock); 183 tim = untimeout(tmp_id); 184 mutex_enter(&t->t_delay_lock); 185 } 186 mutex_exit(&t->t_delay_lock); 187 return (tim); 188 } 189 190 int 191 lwp_timer_copyout(lwp_timer_t *lwptp, int error) 192 { 193 timespec_t rmtime; 194 timespec_t now; 195 196 if (lwptp->lwpt_tsp == NULL) /* nothing to do */ 197 return (error); 198 199 rmtime.tv_sec = rmtime.tv_nsec = 0; 200 if (error != ETIME) { 201 gethrestime(&now); 202 if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) || 203 ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) && 204 (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) { 205 rmtime = lwptp->lwpt_rqtime; 206 timespecsub(&rmtime, &now); 207 } 208 } 209 if (curproc->p_model == DATAMODEL_NATIVE) { 210 if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t))) 211 error = EFAULT; 212 } else { 213 timespec32_t rmtime32; 214 215 TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime); 216 if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t))) 217 error = EFAULT; 218 } 219 220 return (error); 221 } 222