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 2009 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, delta; 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 delta = lwptp->lwpt_rqtime; 72 timespecsub(&delta, &now); 73 lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME, 74 lwp_timer_timeout, lwptp, ts2hrt(&delta), nsec_per_tick, 75 (CALLOUT_FLAG_HRESTIME | CALLOUT_FLAG_ROUNDUP)); 76 } else { 77 /* 78 * Set the thread running only if it is asleep on 79 * its lwpchan sleep queue (not if it is asleep on 80 * the t_delay_lock mutex). 81 */ 82 thread_lock(t); 83 /* do this for the benefit of upi mutexes */ 84 (void) atomic_cas_uint(&lwptp->lwpt_imm_timeout, 0, 1); 85 if (t->t_state == TS_SLEEP && 86 (t->t_flag & T_WAKEABLE) && 87 t->t_wchan0 != NULL) 88 setrun_locked(t); 89 thread_unlock(t); 90 } 91 mutex_exit(&t->t_delay_lock); 92 } 93 94 int 95 lwp_timer_copyin(lwp_timer_t *lwptp, timespec_t *tsp) 96 { 97 timespec_t now; 98 int error = 0; 99 100 if (tsp == NULL) /* not really an error, just need to bzero() */ 101 goto err; 102 lwptp->lwpt_timecheck = timechanged; /* do this before gethrestime() */ 103 gethrestime(&now); /* do this before copyin() */ 104 if (curproc->p_model == DATAMODEL_NATIVE) { 105 if (copyin(tsp, &lwptp->lwpt_rqtime, sizeof (timespec_t))) { 106 error = EFAULT; 107 goto err; 108 } 109 } else { 110 timespec32_t ts32; 111 if (copyin(tsp, &ts32, sizeof (timespec32_t))) { 112 error = EFAULT; 113 goto err; 114 } 115 TIMESPEC32_TO_TIMESPEC(&lwptp->lwpt_rqtime, &ts32); 116 } 117 if (itimerspecfix(&lwptp->lwpt_rqtime)) { 118 error = EINVAL; 119 goto err; 120 } 121 /* 122 * Unless the requested timeout is zero, 123 * get the precise future (absolute) time at 124 * which we are to time out and return ETIME. 125 * We must not return ETIME before that time. 126 */ 127 if (lwptp->lwpt_rqtime.tv_sec == 0 && lwptp->lwpt_rqtime.tv_nsec == 0) { 128 bzero(lwptp, sizeof (lwp_timer_t)); 129 lwptp->lwpt_imm_timeout = 1; 130 } else { 131 lwptp->lwpt_thread = curthread; 132 lwptp->lwpt_tsp = tsp; 133 lwptp->lwpt_time_error = 0; 134 lwptp->lwpt_id = 0; 135 lwptp->lwpt_imm_timeout = 0; 136 timespecadd(&lwptp->lwpt_rqtime, &now); 137 } 138 return (0); 139 err: 140 bzero(lwptp, sizeof (lwp_timer_t)); 141 lwptp->lwpt_time_error = error; 142 return (error); 143 } 144 145 int 146 lwp_timer_enqueue(lwp_timer_t *lwptp) 147 { 148 timespec_t now, delta; 149 150 ASSERT(lwptp->lwpt_thread == curthread); 151 ASSERT(MUTEX_HELD(&curthread->t_delay_lock)); 152 gethrestime(&now); 153 if (lwptp->lwpt_timecheck == timechanged && 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_imm_timeout = 0; 161 delta = lwptp->lwpt_rqtime; 162 timespecsub(&delta, &now); 163 lwptp->lwpt_id = timeout_generic(CALLOUT_REALTIME, 164 lwp_timer_timeout, lwptp, ts2hrt(&delta), nsec_per_tick, 165 (CALLOUT_FLAG_HRESTIME | CALLOUT_FLAG_ROUNDUP)); 166 return (0); 167 } 168 169 /* 170 * Time has already run out or someone reset the system time; 171 * just cause an immediate timeout. 172 */ 173 lwptp->lwpt_imm_timeout = 1; 174 return (1); 175 } 176 177 clock_t 178 lwp_timer_dequeue(lwp_timer_t *lwptp) 179 { 180 kthread_t *t = curthread; 181 clock_t tim = -1; 182 callout_id_t tmp_id; 183 184 mutex_enter(&t->t_delay_lock); 185 while ((tmp_id = lwptp->lwpt_id) != 0) { 186 lwptp->lwpt_id = 0; 187 mutex_exit(&t->t_delay_lock); 188 tim = untimeout_default(tmp_id, 0); 189 mutex_enter(&t->t_delay_lock); 190 } 191 mutex_exit(&t->t_delay_lock); 192 return (tim); 193 } 194 195 int 196 lwp_timer_copyout(lwp_timer_t *lwptp, int error) 197 { 198 timespec_t rmtime; 199 timespec_t now; 200 201 if (lwptp->lwpt_tsp == NULL) /* nothing to do */ 202 return (error); 203 204 rmtime.tv_sec = rmtime.tv_nsec = 0; 205 if (error != ETIME) { 206 gethrestime(&now); 207 if ((now.tv_sec < lwptp->lwpt_rqtime.tv_sec) || 208 ((now.tv_sec == lwptp->lwpt_rqtime.tv_sec) && 209 (now.tv_nsec < lwptp->lwpt_rqtime.tv_nsec))) { 210 rmtime = lwptp->lwpt_rqtime; 211 timespecsub(&rmtime, &now); 212 } 213 } 214 if (curproc->p_model == DATAMODEL_NATIVE) { 215 if (copyout(&rmtime, lwptp->lwpt_tsp, sizeof (timespec_t))) 216 error = EFAULT; 217 } else { 218 timespec32_t rmtime32; 219 220 TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime); 221 if (copyout(&rmtime32, lwptp->lwpt_tsp, sizeof (timespec32_t))) 222 error = EFAULT; 223 } 224 225 return (error); 226 } 227