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 2008 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 * Requeue the timeout if no one has reset the system time 63 * and if the absolute future time has not been reached. 64 */ 65 if (lwptp->lwpt_timecheck == timechanged && 66 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec || 67 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec && 68 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) { 69 lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp, 70 timespectohz(&lwptp->lwpt_rqtime, now)); 71 } else { 72 /* 73 * Set the thread running only if it is asleep on 74 * its lwpchan sleep queue (not if it is asleep on 75 * the t_delay_lock mutex). 76 */ 77 thread_lock(t); 78 if (t->t_state == TS_SLEEP && 79 (t->t_flag & T_WAKEABLE) && 80 t->t_wchan0 != NULL) 81 setrun_locked(t); 82 thread_unlock(t); 83 } 84 mutex_exit(&t->t_delay_lock); 85 } 86 87 int 88 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp) 89 { 90 timespec_t now; 91 int error = 0; 92 93 if (tsp == NULL) /* not really an error, just need to bzero() */ 94 goto err; 95 lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */ 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 } 131 return (0); 132 err: 133 bzero(lwptp, sizeof (lwp_timer_t)); 134 lwptp->lwpt_time_error = error; 135 return (error); 136 } 137 138 int 139 lwp_timer_enqueue(lwp_timer_t *lwptp) 140 { 141 timespec_t now; 142 143 ASSERT(lwptp->lwpt_thread == curthread); 144 ASSERT(MUTEX_HELD(&curthread->t_delay_lock)); 145 gethrestime(&now); 146 if (lwptp->lwpt_timecheck == timechanged && 147 (lwptp->lwpt_rqtime.tv_sec > now.tv_sec || 148 (lwptp->lwpt_rqtime.tv_sec == now.tv_sec && 149 lwptp->lwpt_rqtime.tv_nsec > now.tv_nsec))) { 150 /* 151 * Queue the timeout. 152 */ 153 lwptp->lwpt_id = realtime_timeout(lwp_timer_timeout, lwptp, 154 timespectohz(&lwptp->lwpt_rqtime, now)); 155 return (0); 156 } 157 158 /* 159 * Time has already run out or someone reset the system time; 160 * just cause an immediate timeout. 161 */ 162 lwptp->lwpt_imm_timeout = 1; 163 return (1); 164 } 165 166 clock_t 167 lwp_timer_dequeue(lwp_timer_t *lwptp) 168 { 169 kthread_t *t = curthread; 170 clock_t tim = -1; 171 timeout_id_t tmp_id; 172 173 mutex_enter(&t->t_delay_lock); 174 while ((tmp_id = lwptp->lwpt_id) != 0) { 175 lwptp->lwpt_id = 0; 176 mutex_exit(&t->t_delay_lock); 177 tim = untimeout(tmp_id); 178 mutex_enter(&t->t_delay_lock); 179 } 180 mutex_exit(&t->t_delay_lock); 181 return (tim); 182 } 183 184 int 185 lwp_timer_copyout(lwp_timer_t *lwptp, int error) 186 { 187 timespec_t rmtime; 188 timespec_t now; 189 190 if (lwptp->lwpt_tsp == NULL) /* nothing to do */ 191 return (error); 192 193 rmtime.tv_sec = rmtime.tv_nsec = 0; 194 if (error != ETIME) { 195 gethrestime(&now); 196 if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) || 197 ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) && 198 (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) { 199 rmtime = lwptp->lwpt_rqtime; 200 timespecsub(&rmtime, &now); 201 } 202 } 203 if (curproc->p_model == DATAMODEL_NATIVE) { 204 if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t))) 205 error = EFAULT; 206 } else { 207 timespec32_t rmtime32; 208 209 TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime); 210 if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t))) 211 error = EFAULT; 212 } 213 214 return (error); 215 } 216