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, 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	"@(#)lwp_timer.c	1.4	07/04/26 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