Home | History | Annotate | Download | only in syscall
      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