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