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