The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/mach_timer.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* 
    2  * Mach Operating System
    3  * Copyright (c) 1993,1992 Carnegie Mellon University
    4  * All Rights Reserved.
    5  * 
    6  * Permission to use, copy, modify and distribute this software and its
    7  * documentation is hereby granted, provided that both the copyright
    8  * notice and this permission notice appear in all copies of the
    9  * software, derivative works or modified versions, and any portions
   10  * thereof, and that both notices appear in supporting documentation.
   11  * 
   12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   15  * 
   16  * Carnegie Mellon requests users of this software to return to
   17  * 
   18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   19  *  School of Computer Science
   20  *  Carnegie Mellon University
   21  *  Pittsburgh PA 15213-3890
   22  * 
   23  * any improvements or extensions that they make and grant Carnegie Mellon
   24  * the rights to redistribute these changes.
   25  */
   26 /* 
   27  * HISTORY
   28  * $Log:        mach_timer.c,v $
   29  * Revision 2.2  93/11/17  17:15:58  dbg
   30  *      Use thread_deallocate_nowait to remove thread reference as early
   31  *      as possible.  Move syscall_timer_sleep here and make it use
   32  *      continuations.
   33  *      [93/07/21            dbg]
   34  * 
   35  *      Changed timer_arm_internal to timer_sleep_periodic.
   36  *      Added BUSY bit to show that timer is either enqueued
   37  *      or in timeout routine.
   38  *      [93/07/15            dbg]
   39  * 
   40  *      If the expiration time is before the current time, do the
   41  *      timeout action immediately.
   42  *      [93/07/14            dbg]
   43  * 
   44  *      Return actual wakeup time for timer_sleep.
   45  *      [93/07/13            dbg]
   46  * 
   47  *      Added timer_start_time.
   48  *      [93/07/01            dbg]
   49  * 
   50  *      Changed timer_ipc_lock to timer_lock, timer_lock to
   51  *      timer_ref_lock.  Added mach_timer_thread_reference,
   52  *      mach_timer_thread_deallocate, so that a timer that is
   53  *      in use by a thread will remain even if all send rights
   54  *      vanish.
   55  *      [93/06/24            dbg]
   56  * 
   57  *      Use clock_read to read clock without locking.
   58  *      [93/06/18            dbg]
   59  * 
   60  *      Add interval parameter to timer_arm_internal.
   61  *      [93/05/12            dbg]
   62  * 
   63  *      Increment overrun count in timer instead of queuing repeated
   64  *      messages to an overflowing expire port.
   65  *      [93/05/06            dbg]
   66  * 
   67  *      Use normal waiting mechanism to wait for timer expiration
   68  *      instead of thread_suspend.
   69  *      [93/04/02            dbg]
   70  * 
   71  *      Timer_lock does not need splhigh.
   72  *      [93/02/26            dbg]
   73  * 
   74  *      Merged into kernel mainline.
   75  * 
   76  *      Make sure time_specs are valid
   77  *      [92/09/11       savage]
   78  *      Fixed race with task_lock in task_timers.
   79  *      [92/07/31       savage]
   80  *      Added TIMER_THREAD_SUSPEND.  Needs fix since there is a possible 
   81  *      priority inversion when the thread associated with timer is 
   82  *      uninterruptible.
   83  *      [92/07/30       savage]
   84  *      Changed time_values_t to time_spec_t to account for timer hardware skew.
   85  *      Changed clock parameter in timer_create to clock_t instead of constant.
   86  *      [92/07/18       savage]
   87  *      Added timer_info, and task_timers
   88  *      [92/07/02       savage]
   89  *      Added timer_cancel, cleaned up flags & types, and fixed bug with
   90  *      freeing event and send right resources.  Turns out we do need
   91  *      the reference counting :-)
   92  *      [92/06/22       savage]
   93  *      Added timer_sleep and timer_get_evc for performance reasons.
   94  *      [92/06/17       savage]
   95  *      Changed all references to timer_t to mach_timer_t and renamed
   96  *      timer_init to mach_timer_init... timer_t really should be counter_t
   97  *      [92/06/13       savage]
   98  *      Exported to user (with significant "borrowing" from thread.c)
   99  *      Added timer_arm, timer_create, timer_terminate, and timer_deallocate
  100  *      I'm not sure about the reference counting... do we really need this?
  101  *      [92/06/11       savage]
  102  *      Created 
  103  *      [92/05/01       savage]
  104  * 
  105  */
  106 
  107 /*
  108  *      Timer alarm object, to provide accurate alarm services.
  109  *      Depends on a clock object.
  110  */
  111 
  112 #include <mach/boolean.h>
  113 #include <mach/kern_return.h>
  114 #include <mach/time_spec.h>
  115 #include <mach/notify.h>
  116 #include <device/device_types.h>
  117 #include <ipc/ipc_port.h>
  118 #include <ipc/ipc_space.h>
  119 #include <kern/clock.h>
  120 #include <kern/eventcount.h>
  121 #include <kern/kern_io.h>
  122 #include <kern/queue.h>
  123 #include <kern/lock.h>
  124 #include <kern/mach_param.h>
  125 #include <kern/mach_timer.h>
  126 #include <kern/sched_prim.h>
  127 #include <kern/task.h>
  128 #include <kern/zalloc.h>
  129 #include <kern/memory.h>
  130 #include <machine/machspl.h>    /* For def'n of splsched() */
  131 
  132 struct zone             *timer_zone;
  133 struct mach_timer       timer_template;
  134 
  135 /*
  136  *      Expiration routines
  137  */
  138 void timer_expire_synch(void *);        /* wakeup */
  139 void timer_expire_event(void *);        /* event signal */
  140 void timer_expire_message(void *);      /* message/suspend */
  141 
  142 /*
  143  *      Routine:        mach_timer_init
  144  *      Purpose:        Initialize memory, queues and locks for timer package
  145  *      Conditions:     None
  146  */
  147 void mach_timer_init(void)
  148 {
  149         timer_zone = zinit(
  150                         sizeof(mach_timer_data_t),
  151                         TIMER_MAX * sizeof(mach_timer_data_t),
  152                         TIMER_CHUNK * sizeof(mach_timer_data_t),
  153                         FALSE, "timers");
  154 
  155         /* timer_template.tm_chain (later) */
  156         timer_template.tm_expire_time.seconds = 0;
  157         timer_template.tm_expire_time.nanoseconds = 0;
  158         /* timer_template.tm_fcn (later) */
  159         /* timer_template.tm_param (later) */
  160         timer_template.tm_flags = TELT_UNSET;
  161         /* timer_template.tm_clock (later) */
  162         timer_template.tm_period.seconds = 0;
  163         timer_template.tm_period.nanoseconds = 0;
  164 
  165         /* timer_template.tm_self (later) */
  166         /* timer_template.tm_lock (later) */
  167         /* timer_template.tm_ref_lock (later) */
  168 
  169         /* one ref for being alive, the other for the caller */
  170         timer_template.tm_ref_count = 2;
  171 
  172         timer_template.tm_misc = TM_ACTIVE;
  173         timer_template.tm_expire = IP_NULL;
  174 
  175         /* No event allocated yet... */
  176         timer_template.tm_event.ev_id = 0;
  177         timer_template.tm_event.sanity = ((struct evc *)0);
  178 
  179         timer_template.tm_thread = THREAD_NULL;
  180         timer_template.tm_overruns = 0;
  181 }
  182 
  183 /*
  184  *      Routine:        mach_timer_deallocate
  185  *      Purpose:        Decrement timer reference count; if it's zero then
  186  *                      free up the kernel resources associated with the
  187  *                      timer.
  188  *      Conditions:     None
  189  */
  190 void mach_timer_deallocate(
  191         mach_timer_t    timer)
  192 {
  193         if (timer == TIMER_NULL)
  194             return;
  195 
  196         timer_ref_lock(timer);
  197         if (--timer->tm_ref_count > 0) {
  198                 timer_ref_unlock(timer);
  199                 return;
  200         }
  201         timer_ref_unlock(timer);
  202 
  203         assert(!(timer->tm_misc & TM_ACTIVE));
  204         assert(timer->tm_flags == TELT_UNSET);
  205 
  206         /* give up kernel memory */
  207         zfree(timer_zone, (vm_offset_t) timer);
  208 }
  209 
  210 
  211 /*
  212  *      Routine:        timer_reference
  213  *      Purpose:        Increment timer reference count
  214  *      Conditions:     None
  215  */
  216 void mach_timer_reference(
  217         mach_timer_t    timer)
  218 {
  219         if (timer == TIMER_NULL)
  220             return;
  221 
  222         timer_ref_lock(timer);
  223         timer->tm_ref_count++;
  224         timer_ref_unlock(timer);
  225 }
  226 
  227 /*
  228  *      Routine:        convert_port_to_timer
  229  *      Purpose:        Convert from a port to a timer.  If the port isn't
  230  *                      valid, or doesn't represent a timer then return
  231  *                      TIMER_NULL.  Produces a timer reference.
  232  *      Conditions:     None
  233  */
  234 mach_timer_t convert_port_to_timer(
  235         ipc_port_t port)
  236 {
  237         mach_timer_t timer = TIMER_NULL;
  238 
  239         if (IP_VALID(port)) {
  240                 ip_lock(port);
  241                 if (ip_active(port) &&
  242                     (ip_kotype(port) == IKOT_TIMER)) {
  243                         timer = (mach_timer_t) port->ip_kobject;
  244                         mach_timer_reference(timer);
  245                 }
  246                 ip_unlock(port);
  247         }
  248 
  249         return timer;
  250 }
  251 
  252 
  253 /*
  254  *      Routine:        convert_timer_to_port
  255  *      Purpose:        Convert from a port to a timer.  Consumes a timer
  256  *                      reference.
  257  *      Conditions:     None
  258  */
  259 ipc_port_t convert_timer_to_port(
  260         mach_timer_t timer)
  261 {
  262         ipc_port_t port;
  263 
  264         timer_lock(timer);
  265 
  266         if (timer->tm_misc & TM_ACTIVE) {
  267             assert(timer->tm_self != IP_NULL);
  268             port = ipc_port_make_send(timer->tm_self);
  269         } else {
  270             port = IP_NULL;
  271         }
  272 
  273         timer_unlock(timer);
  274 
  275         mach_timer_deallocate(timer);
  276         return port;
  277 }
  278 
  279 /*
  280  *      Routine:        timer_disarm
  281  *      Purpose:        SAFELY take a timer off its queue, wakeup any
  282  *                      threads waiting on it, and free any expired
  283  *                      send rights.
  284  *      Conditions:     Caller should hold the timer lock.
  285  */
  286 void timer_disarm(
  287         mach_timer_t    timer)
  288 {
  289         if (timer_elt_dequeue(&timer->te)) {
  290             /*
  291              *  Was on queue.  If a thread is waiting
  292              *  for the timer, wake it up.
  293              */
  294             if (timer->tm_fcn == timer_expire_synch)
  295                 timer_expire_synch(timer->tm_param);
  296 
  297             /*
  298              *  Timer is no longer busy.
  299              */
  300             timer->tm_misc &= ~TM_BUSY;
  301         }
  302 }
  303 
  304 
  305 /*
  306  *      Routine:        timer_create
  307  *      Purpose:        Allocate a new timer object in the kernel,
  308  *                      and enable its ipc connection to the world.
  309  *
  310  *      Conditions:     None
  311  */
  312 kern_return_t timer_create(
  313         register mach_clock_t   clock,
  314         register mach_timer_t   *timer)
  315 {
  316         register mach_timer_t   new_timer;
  317         ipc_port_t      kport;
  318         ipc_port_t      kport_send_once;
  319         ipc_port_t      nprev;
  320 
  321         if (clock == CLOCK_NULL)
  322             return KERN_INVALID_ARGUMENT;
  323 
  324         new_timer = (mach_timer_t) zalloc(timer_zone);
  325         if (new_timer == TIMER_NULL)
  326             return KERN_RESOURCE_SHORTAGE;
  327 
  328         *new_timer = timer_template;
  329 
  330         /* timeout routine parameter is always timer itself */
  331         new_timer->tm_param = new_timer;
  332 
  333         /* which clock device is responsible for this timer */
  334         new_timer->tm_clock = clock;
  335 
  336         /* for serialization of access to a particular timer */
  337         simple_lock_init(&new_timer->tm_lock);
  338         simple_lock_init(&new_timer->tm_ref_lock);
  339 
  340         /* setup IPC structures and let it talk to the world */
  341 
  342         kport = ipc_port_alloc_kernel();
  343         if (kport == IP_NULL) {
  344             /*
  345              *  No resources
  346              */
  347             zfree(timer_zone, (vm_offset_t) new_timer);
  348             return KERN_RESOURCE_SHORTAGE;
  349         }
  350 
  351         new_timer->tm_self = kport;
  352         ipc_kobject_set(kport, (ipc_kobject_t) new_timer, IKOT_TIMER);
  353 
  354         /*
  355          *      Add no-more-senders notify request, sent
  356          *      to timer port.  Since current make-send count
  357          *      is 0, sync must be 1 to avoid an immediate
  358          *      notification.  One send right will be created
  359          *      when the timer port is returned to caller.
  360          */
  361         kport_send_once = ipc_port_make_sonce(kport);
  362         ip_lock(kport);
  363         ipc_port_nsrequest(kport, 1, kport_send_once, &nprev);
  364                         /* unlocks kport */
  365         assert(nprev == IP_NULL);
  366 
  367         *timer = new_timer;
  368         return KERN_SUCCESS;
  369 }
  370 
  371 /*
  372  *      Internal routine to terminate a timer, shared between
  373  *      timer_terminate and timer_no_more_senders.
  374  *
  375  *      Timer is locked on entry; will be unlocked (and perhaps
  376  *      deallocated) on exit.
  377  */
  378 void timer_terminate_internal(
  379         mach_timer_t timer)
  380 {
  381         ipc_port_t      kport;
  382         thread_t        old_thread;
  383 
  384         /*
  385          *      Remove timer from queue.
  386          */
  387         timer_disarm(timer);
  388 
  389         /*
  390          *      Mark timer inactive, and remove its port.
  391          */
  392         timer->tm_misc &= ~TM_ACTIVE;
  393         kport = timer->tm_self;
  394         timer->tm_self = IP_NULL;
  395         ipc_kobject_set(timer->tm_self, IKO_NULL, IKOT_NONE);
  396 
  397         /*
  398          *      Free event resources
  399          */
  400         if (timer->tm_misc & TM_EVENT_ALLOC)
  401             evc_destroy(&timer->tm_event);
  402 
  403         old_thread = timer->tm_thread;
  404         timer_unlock(timer);
  405 
  406         /*
  407          *      Release reference and send right for expire port.
  408          */
  409         if (IP_VALID(timer->tm_expire))
  410             ipc_port_release_send(timer->tm_expire);
  411 
  412         /*
  413          *      Deallocate the timer`s thread reference
  414          *      if there was one.
  415          */
  416         if (old_thread != THREAD_NULL)
  417             thread_deallocate(old_thread);
  418 
  419         /*
  420          *      Deallocate the timer port, and the timer structure
  421          *      itself.
  422          */
  423         ipc_port_dealloc_kernel(kport);
  424         mach_timer_deallocate(timer);
  425 }
  426 
  427 
  428 /*
  429  *      Routine:        timer_terminate
  430  *      Purpose:        Destroy a timer.  If the timer is already being killed
  431  *                      then we will return KERN_FAILURE
  432  *      Conditions:     None
  433  */
  434 kern_return_t timer_terminate(
  435         register mach_timer_t   timer)
  436 {
  437         if (timer == TIMER_NULL)
  438             return KERN_INVALID_ARGUMENT;
  439            
  440         timer_lock(timer);
  441 
  442         /*
  443          *      Is it already being killed?  If not then mark it as such.
  444          */
  445         if (!(timer->tm_misc & TM_ACTIVE)) {
  446             timer_unlock(timer);
  447             return KERN_FAILURE;
  448         }
  449 
  450         /*
  451          *      Destroy the timer (code is shared with no-more-senders)
  452          */
  453         timer_terminate_internal(timer);
  454 
  455         return KERN_SUCCESS;
  456 }
  457 
  458 /*
  459  *      Routine:        timer_thread_reference
  460  *      Purpose:
  461  *              Create a internal reference to a timer from a thread,
  462  *              to use the timer as the deadline or wakeup timer for
  463  *              the thread.  This reference must keep the timer alive
  464  *              just as a send-once reference would; further rights
  465  *              to the timer port can be obtained from the thread.
  466  *
  467  *      Implementation:
  468  *              Make a new send right to the timer port.
  469  */
  470 boolean_t
  471 mach_timer_thread_reference(
  472         mach_timer_t    timer)
  473 {
  474         if (timer == TIMER_NULL)
  475             return TRUE;
  476 
  477         timer_lock(timer);
  478         if (!(timer->tm_misc & TM_ACTIVE)) {
  479             timer_unlock(timer);
  480             return FALSE;       /* timer died suddenly */
  481         }
  482 
  483         /*
  484          *      Make a new send right to the timer port.
  485          */
  486         (void) ipc_port_make_send(timer->tm_self);
  487         timer_unlock(timer);
  488 
  489         return TRUE;
  490 }
  491 
  492 /*
  493  *      Routine:        timer_thread_deallocate
  494  *      Purpose:
  495  *              Remove an internal reference to a timer from a thread.
  496  *              If the timer now has no more send rights to its kernel
  497  *              port, terminate the timer.
  498  *
  499  *      Implementation:
  500  *              Release the corresponding send right for the timer
  501  *              port.
  502  */
  503 void mach_timer_thread_deallocate(
  504         mach_timer_t    timer)
  505 {
  506         ipc_port_t      kport;
  507 
  508         if (timer == TIMER_NULL)
  509             return;
  510 
  511         timer_lock(timer);
  512         assert(timer->tm_misc & TM_ACTIVE);
  513 
  514         /*
  515          *      Release a send right to the timer port.
  516          *      Since this may send a no-more-senders
  517          *      message, we must unlock the timer first.
  518          *      The port is safe, since it has an extra
  519          *      reference corresponding to the send right
  520          *      that we are releasing.  ipc_port_release_send
  521          *      will also release the reference.
  522          */
  523         kport = timer->tm_self;
  524         timer_unlock(timer);
  525 
  526         ipc_port_release_send(kport);
  527 }
  528 
  529 
  530 /*
  531  *      Routine:        timer_get_evc
  532  *      Purpose:        Associate timer with event handle and pass it back to
  533  *                      the user.  This is a significant performance
  534  *                      improvement over a message, but it conveys less
  535  *                      information and is only quasi-supported.
  536  *      Conditions:     None.
  537  */
  538 kern_return_t timer_get_evc(
  539         register mach_timer_t   timer,
  540         natural_t               *event)
  541 {
  542         if (timer == TIMER_NULL)
  543             return KERN_INVALID_ARGUMENT;
  544 
  545         timer_lock(timer);
  546 
  547         /*
  548          *      Do we already have an event?
  549          */
  550         if (timer->tm_misc & TM_EVENT_ALLOC) {
  551             timer_unlock(timer);
  552             return KERN_SUCCESS;
  553         }
  554 
  555         if (evc_init(&timer->tm_event) != KERN_SUCCESS) {
  556             timer_unlock(timer);
  557             return KERN_RESOURCE_SHORTAGE;
  558         }
  559 
  560         *event = timer->tm_event.ev_id;
  561         
  562         /* Timer now owns event */
  563         timer->tm_misc |= TM_EVENT_ALLOC;
  564 
  565         timer_unlock(timer);
  566 
  567         return KERN_SUCCESS;
  568 }
  569 
  570 
  571 /*
  572  *      Routine:        timer_arm
  573  *      Purpose:        Attempts to set a timer and enqueue it in a clock 
  574  *                      queue.  This call may fail if the timer is already 
  575  *                      enqueued, if it is being killed, if the port specified
  576  *                      for timer expiration messages is invalid, if the user
  577  *                      specifies an event timer without allocating an event
  578  *                      first, or if the specified expiration time has
  579  *                      already passed.
  580  *      Conditions:     None
  581  */
  582 kern_return_t timer_arm(
  583         register mach_timer_t   timer,
  584         time_spec_t             expire_time,
  585         time_spec_t             interval_time,
  586         register ipc_port_t     expire_port,
  587         thread_t                thread,
  588         register int            flags)
  589 {       
  590         if (timer == TIMER_NULL)
  591             return KERN_INVALID_ARGUMENT;
  592 
  593         if (!time_spec_valid(expire_time) ||
  594             ((flags & TIMER_PERIODIC) && !time_spec_valid(interval_time)))
  595         {
  596             return KERN_INVALID_VALUE;
  597         }
  598 
  599         timer_lock(timer);
  600 
  601         /*
  602          *      Is the timer currently being killed or is it enqueued?
  603          */
  604         if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
  605             timer_unlock(timer);
  606             return KERN_FAILURE;
  607         }
  608 
  609         /*
  610          *      If TIMER_PERIODIC flag is set then the interval_time argument
  611          *      must be more than the clock resolution.
  612          */
  613         if (flags & TIMER_PERIODIC) {
  614             if (interval_time.seconds == 0 &&
  615                 interval_time.nanoseconds < timer->tm_clock->resolution)
  616             {
  617                 timer_unlock(timer);
  618                 return KERN_INVALID_VALUE;
  619             }
  620             timer->tm_period = interval_time;
  621             timer->tm_flags |= TELT_PERIODIC;
  622         }
  623         else {
  624             timer->tm_flags &= ~TELT_PERIODIC;
  625         }
  626 
  627         /*
  628          *      If the user claims that this is an event timer then make
  629          *      sure he's allocated the event already.
  630          */  
  631         if (flags & TIMER_EVENT) {
  632             if (!(timer->tm_misc & TM_EVENT_ALLOC)) {
  633                 timer_unlock(timer);
  634                 return KERN_INVALID_ARGUMENT;
  635             }
  636             timer->tm_event.count = 0;
  637             timer->tm_fcn = timer_expire_event;
  638         }
  639         else {
  640             /*
  641              *  It's a Message timer... now check to see if
  642              *  the expire port is valid?
  643              */
  644             if (!IP_VALID(expire_port)) {
  645                 timer_unlock(timer);
  646                 return KERN_INVALID_CAPABILITY;
  647             }
  648             /*
  649              *  Donate expire port reference and send right to timer
  650              */
  651             assert(timer->tm_expire == IP_NULL);
  652             timer->tm_expire = expire_port;
  653             timer->tm_fcn = timer_expire_message;
  654         }
  655                 
  656         /*
  657          *      Save the thread to be suspended 
  658          */
  659         if (flags & TIMER_THREAD_SUSPEND) {
  660             if (thread == THREAD_NULL) {
  661                 timer->tm_expire = IP_NULL;     /* error return will dealloc */
  662                 timer_unlock(timer);
  663                 return KERN_INVALID_ARGUMENT;
  664             }
  665             assert(timer->tm_thread == THREAD_NULL);
  666             timer->tm_thread = thread;
  667             timer->tm_misc |= TM_SUSPEND;
  668         }
  669 
  670         /*
  671          *      Mark the timer busy, and put it on the
  672          *      clock`s timeout queue.
  673          */
  674         timer->tm_misc |= TM_BUSY;
  675         if (!timer_elt_enqueue(&timer->te,
  676                                expire_time,
  677                                (flags & TIMER_ABSOLUTE) != 0))
  678         {
  679             /*
  680              *  Time value was less than current time.
  681              *  Make the timer expire immediately.
  682              */
  683             timer_unlock(timer);
  684             (*timer->tm_fcn)(timer->tm_param);  /* resets 'busy' */
  685         }
  686         else {
  687             timer_unlock(timer);
  688         }
  689 
  690         return KERN_SUCCESS;
  691 }
  692 
  693 
  694 /*
  695  *      Routine:        timer_sleep
  696  *      Purpose:        Attempts to set a timer and enqueue it in a clock 
  697  *                      queue.  Similar to timer_arm except that the calling
  698  *                      thread is blocked on on the timer and will be awakened
  699  *                      when the timer expires.
  700  *      Conditions:     None
  701  */
  702 kern_return_t timer_sleep(
  703         mach_timer_t    timer,
  704         time_spec_t     expire_time,
  705         int             flags,
  706         time_spec_t     *wakeup_time)
  707 {       
  708         kern_return_t   kr;
  709 
  710         if (timer == TIMER_NULL)
  711             return KERN_INVALID_ARGUMENT;
  712 
  713         if (!time_spec_valid(expire_time))
  714             return KERN_INVALID_VALUE;
  715 
  716         timer_lock(timer);
  717 
  718         /*
  719          *      Is the timer currently enqueued or being killed?
  720          */
  721         if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
  722             timer_unlock(timer);
  723             return KERN_FAILURE;
  724         }
  725 
  726         assert(timer->tm_thread == THREAD_NULL);
  727         timer->tm_thread = current_thread();
  728         thread_reference(timer->tm_thread);     /* for timer */
  729         timer->tm_fcn = timer_expire_synch;
  730 
  731         timer->tm_misc |= TM_BUSY;
  732         if (!timer_elt_enqueue(&timer->te,
  733                                expire_time,
  734                                (flags & TIMER_ABSOLUTE) != 0))
  735         {
  736             /*
  737              *  Time has already passed - don`t sleep.
  738              */
  739             timer->tm_misc &= ~TM_BUSY;
  740             timer_unlock(timer);
  741             kr = KERN_SUCCESS;
  742         }
  743         else {
  744             /*
  745              *  Wait for timer to expire.
  746              */
  747             thread_will_wait(current_thread());
  748             timer_unlock(timer);
  749             thread_block(0);
  750             kr = (current_thread()->wait_result == THREAD_INTERRUPTED)
  751                         ? KERN_ABORTED
  752                         : KERN_SUCCESS;
  753         }
  754 
  755         clock_read(*wakeup_time, timer->tm_clock);
  756 
  757         return kr;
  758 }
  759 
  760 /*
  761  *      Routine:        syscall_timer_sleep
  762  *      Purpose:        same as timer_sleep, but called as a trap.
  763  *                      Uses a continuation.
  764  */
  765 
  766 struct timer_sleep_save {
  767         mach_timer_t    timer;
  768         time_spec_t     *wakeup_time;
  769 };
  770 
  771 #define SAVE(thread)    ((struct timer_sleep_save *)&(thread)->saved)
  772 
  773 no_return
  774 timer_sleep_continue(void)
  775 {
  776         thread_t        thread = current_thread();
  777         mach_timer_t    timer = SAVE(thread)->timer;
  778         time_spec_t     *wakeup_time = SAVE(thread)->wakeup_time;
  779         kern_return_t   kr = (thread->wait_result == THREAD_INTERRUPTED)
  780                                 ? KERN_ABORTED
  781                                 : KERN_SUCCESS;
  782         time_spec_t     wake_time;
  783 
  784         clock_read(wake_time, timer->tm_clock);
  785         mach_timer_deallocate(timer);
  786 
  787         (void) copyout(&wake_time,
  788                        wakeup_time,
  789                        sizeof(time_spec_t));
  790         thread_syscall_return(kr);
  791         /*NOTREACHED*/
  792 }
  793 
  794 extern mach_timer_t port_name_to_timer(mach_port_t);
  795 
  796 kern_return_t syscall_timer_sleep(
  797         mach_port_t     timer_port,
  798         time_spec_t     expire_time,
  799         int             flags,
  800         time_spec_t     *wakeup_time)
  801 {
  802         thread_t        thread = current_thread();
  803         mach_timer_t    timer;
  804 
  805         timer = port_name_to_timer(timer_port);
  806         if (timer == TIMER_NULL)
  807             return KERN_INVALID_ARGUMENT;
  808 
  809         if (!time_spec_valid(expire_time)) {
  810             mach_timer_deallocate(timer);
  811             return KERN_INVALID_VALUE;
  812         }
  813 
  814         /*
  815          *      Save the values we will need when the
  816          *      continuation is called.
  817          */
  818         SAVE(thread)->timer = timer;
  819         SAVE(thread)->wakeup_time = wakeup_time;
  820 
  821         timer_lock(timer);
  822 
  823         /*
  824          *      Is the timer currently enqueued or being killed?
  825          */
  826         if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
  827             timer_unlock(timer);
  828             mach_timer_deallocate(timer);
  829             return KERN_FAILURE;
  830         }
  831 
  832         assert(timer->tm_thread == THREAD_NULL);
  833         timer->tm_thread = thread;
  834         thread_reference(thread);       /* for timer */
  835         timer->tm_fcn = timer_expire_synch;
  836 
  837         timer->tm_misc |= TM_BUSY;
  838         if (!timer_elt_enqueue(&timer->te,
  839                                expire_time,
  840                                (flags & TIMER_ABSOLUTE) != 0))
  841         {
  842             /*
  843              *  Time has already passed - don`t sleep.
  844              */
  845             timer->tm_misc &= ~TM_BUSY;
  846             timer_unlock(timer);
  847             timer_sleep_continue();
  848             /*NOTREACHED*/
  849         }
  850         else {
  851             /*
  852              *  Wait for timer to expire.
  853              */
  854             thread_will_wait(thread);
  855             timer_unlock(timer);
  856             thread_block_noreturn(timer_sleep_continue);
  857             /*NOTREACHED*/
  858         }
  859 }
  860 
  861 
  862         
  863 /*
  864  *      Routine:        timer_cancel
  865  *      Purpose:        Attempts to cancel a timer which has been enqueued.
  866  *                      If the timer is periodic and flags specifies
  867  *                      TIMER_PERIODIC then only the current iteration of
  868  *                      the timer is canceled.  If a thread is sleeping on
  869  *                      the timer then it will wake up.
  870  *      Conditions:     None
  871  */
  872 kern_return_t timer_cancel(
  873         mach_timer_t    timer,
  874         int             flags)
  875 {
  876         ipc_port_t      expire_port = IP_NULL;
  877         thread_t        thread = THREAD_NULL;
  878 
  879         if (timer == TIMER_NULL)
  880             return KERN_INVALID_ARGUMENT;
  881 
  882         timer_lock(timer);
  883         
  884         if (!(timer->tm_misc & TM_ACTIVE) || !(timer->tm_misc & TM_BUSY)) {
  885             timer_unlock(timer);
  886             return KERN_FAILURE;
  887         }
  888         
  889         timer_disarm(timer);
  890 
  891         /*
  892          *      If the timer is periodic and the user specified that
  893          *      we should only cancel one iteration of the timer
  894          *      then re-enqueue it.
  895          */
  896 
  897         if ((timer->tm_flags & TELT_PERIODIC) &&
  898             (flags & TIMER_PERIODIC))
  899         {
  900             time_spec_add(timer->tm_expire_time, timer->tm_period);
  901             timer->tm_misc |= TM_BUSY;
  902             (void) timer_elt_enqueue(&timer->te, timer->tm_expire_time, TRUE);
  903         }
  904         else {
  905             /*
  906              *  Mark timer non-periodic - we are cancelling all iterations.
  907              */
  908             timer->tm_flags &= ~TELT_PERIODIC;
  909 
  910             /*
  911              *  Release reference and send right to the expire_port,
  912              *  after we unlock the timer.
  913              */
  914             expire_port = timer->tm_expire;
  915             timer->tm_expire = IP_NULL;
  916 
  917             /*
  918              *  Also release reference to the thread.
  919              */
  920             thread = timer->tm_thread;
  921             timer->tm_thread = THREAD_NULL;
  922         }
  923 
  924         timer_unlock(timer);
  925 
  926         if (IP_VALID(expire_port))
  927             ipc_port_release_send(expire_port);
  928         if (thread != THREAD_NULL)
  929             thread_deallocate(thread);
  930 
  931         return KERN_SUCCESS;
  932 }
  933 
  934 /*
  935  *      Routine:        timer_info
  936  *      Purpose:        Returns the current state of the specified timer.
  937  *      Conditions:     None
  938  */
  939 kern_return_t timer_info(
  940         register mach_timer_t   timer,
  941         int                     flavor,
  942         mach_clock_t            *clock,         /* out */
  943         ipc_port_t              *expire_port,   /* out */
  944         thread_t                *thread,        /* out */
  945         mach_timer_info_t       timer_info_out, /* pointer to OUT array */
  946         natural_t               *timer_info_count)      /*IN/OUT*/
  947 {
  948 
  949         if (timer == TIMER_NULL)
  950             return KERN_INVALID_ARGUMENT;
  951 
  952         if (flavor == MACH_TIMER_BASIC_INFO) {
  953             register mach_timer_basic_info_t    basic_info;
  954 
  955             if (*timer_info_count < MACH_TIMER_BASIC_INFO_COUNT) {
  956                 return KERN_INVALID_ARGUMENT;
  957             }
  958 
  959             basic_info = (mach_timer_basic_info_t) timer_info_out;
  960 
  961             timer_lock(timer);
  962 
  963             /*
  964              *  Return ports
  965              */
  966             *clock = timer->tm_clock;
  967 
  968             if (IP_VALID(timer->tm_expire)) {
  969                 *expire_port = ipc_port_copy_send(timer->tm_expire);
  970             } else {
  971                 *expire_port = MACH_PORT_NULL;
  972             }
  973 
  974             if (timer->tm_thread != THREAD_NULL) {
  975                 thread_reference(timer->tm_thread);
  976                 *thread = timer->tm_thread;
  977             } else {
  978                 *thread = MACH_PORT_NULL;
  979             }
  980 
  981             /*
  982              *  Fill in info
  983              */
  984             basic_info->type = 0;
  985             if (timer->tm_flags & TELT_ABSOLUTE)
  986                 basic_info->type |= TIMER_ABSOLUTE;
  987             if (timer->tm_flags & TELT_PERIODIC)
  988                 basic_info->type |= TIMER_PERIODIC;
  989             if (timer->tm_fcn == timer_expire_event)
  990                 basic_info->type |= TIMER_EVENT;
  991             if (timer->tm_misc & TM_SUSPEND)
  992                 basic_info->type |= TIMER_THREAD_SUSPEND;
  993 
  994             basic_info->expire_time = timer->tm_expire_time;
  995             basic_info->interval_time = timer->tm_period;
  996             basic_info->event = timer->tm_event.ev_id;
  997             basic_info->overruns = timer->tm_overruns;
  998             basic_info->enqueued = (timer->tm_misc & TM_BUSY) != 0;
  999 
 1000             timer_unlock(timer);
 1001 
 1002             *timer_info_count = MACH_TIMER_BASIC_INFO_COUNT;
 1003             return KERN_SUCCESS;
 1004         }
 1005         else {
 1006             return KERN_INVALID_ARGUMENT;
 1007         }
 1008 }
 1009 
 1010 /*
 1011  *      What to do when timer expires:
 1012  */
 1013 
 1014 /*
 1015  *      Wakeup thread.
 1016  */
 1017 void timer_expire_synch(void *param)
 1018 {
 1019         mach_timer_t    timer = (mach_timer_t) param;
 1020 
 1021         thread_go(timer->tm_thread);
 1022 
 1023         timer_lock(timer);
 1024         if (!(timer->tm_flags & TELT_PERIODIC)) {
 1025             timer->tm_misc &= ~TM_BUSY;
 1026             thread_deallocate_nowait(timer->tm_thread);
 1027             timer->tm_thread = THREAD_NULL;
 1028         }
 1029         timer_unlock(timer);
 1030 }
 1031 
 1032 /*
 1033  *      Signal event.
 1034  */
 1035 void timer_expire_event(void *param)
 1036 {
 1037         mach_timer_t    timer = (mach_timer_t) param;
 1038 
 1039         evc_signal(&timer->tm_event);
 1040 
 1041         timer_lock(timer);
 1042         if (!(timer->tm_flags & TELT_PERIODIC))
 1043             timer->tm_misc &= ~TM_BUSY;
 1044         timer_unlock(timer);
 1045 }
 1046 
 1047 /*
 1048  *      Send message; optionally suspend the thread.
 1049  */
 1050 extern kern_return_t
 1051 timer_expire(ipc_port_t expire_port, time_spec_t time);
 1052 
 1053 void timer_expire_message(void *param)
 1054 {
 1055         mach_timer_t    timer = (mach_timer_t) param;
 1056         ipc_port_t      expire_port;
 1057         time_spec_t     cur_time;
 1058         thread_t        thread;
 1059 
 1060         thread = timer->tm_thread;
 1061         if (timer->tm_misc & TM_SUSPEND)
 1062             thread_suspend_nowait(thread);
 1063 
 1064         timer_lock(timer);
 1065         expire_port = timer->tm_expire;
 1066 
 1067         if (!IP_VALID(expire_port)) {
 1068             /*
 1069              *  Port expired on the previous iteration,
 1070              *  but we didn`t have the proper locking to
 1071              *  notice it then.  Clear the timer.
 1072              */
 1073             timer->tm_expire = IP_NULL;
 1074             timer->tm_thread = THREAD_NULL;
 1075             timer->tm_flags &= ~TELT_PERIODIC;
 1076             timer->tm_misc &= ~(TM_BUSY | TM_SUSPEND);
 1077             timer_unlock(timer);
 1078 
 1079             if (thread != THREAD_NULL)
 1080                 thread_deallocate_nowait(thread);
 1081         }
 1082 
 1083         else if (timer->tm_flags & TELT_PERIODIC) {
 1084             /*
 1085              *  Check here for port overflow.
 1086              *  If the port is full, don`t send the
 1087              *  message, since that will eventually
 1088              *  fill the kernel with undelivered
 1089              *  timer expirations.  Instead increment
 1090              *  the timer`s overrun counter.
 1091              *
 1092              *  (should be detected by the return code
 1093              *  from timer_expire, but that requires
 1094              *  patching it to not use SEND_ALWAYS.)
 1095              */
 1096             ip_lock(expire_port);
 1097             if (ip_active(expire_port) &&
 1098                 expire_port->ip_receiver != ipc_space_kernel &&
 1099                 expire_port->ip_msgcount >= expire_port->ip_qlimit)
 1100             {
 1101                 /*
 1102                  *      Port is over its queue limit.
 1103                  *      Increment timer overrun counter.
 1104                  */
 1105                 ip_unlock(expire_port);
 1106                 timer->tm_overruns++;
 1107                 timer_unlock(timer);
 1108             }
 1109             else {
 1110                 /*
 1111                  *      Port is OK.
 1112                  *      Clone another send right for the expire
 1113                  *      port (for the next expiration), and send
 1114                  *      the message.
 1115                  */
 1116                 ip_unlock(expire_port);
 1117                 timer->tm_expire = ipc_port_copy_send(expire_port);
 1118                 timer_unlock(timer);
 1119 
 1120                 clock_read(cur_time, timer->tm_clock);
 1121                 (void) timer_expire(expire_port, cur_time);
 1122                                         /* may NOT block */
 1123             }
 1124         }
 1125         else {
 1126             /*
 1127              *  Single expiration.  Move send right
 1128              *  to message, and clear the timer.
 1129              */
 1130             timer->tm_expire = IP_NULL;
 1131             timer->tm_thread = THREAD_NULL;
 1132             timer->tm_misc &= ~(TM_BUSY | TM_SUSPEND);
 1133             timer_unlock(timer);
 1134 
 1135             if (thread != THREAD_NULL)
 1136                 thread_deallocate_nowait(thread);
 1137 
 1138             clock_read(cur_time, timer->tm_clock);
 1139             (void) timer_expire(expire_port, cur_time);
 1140                                         /* may NOT block */
 1141         }
 1142 }
 1143 
 1144 /*
 1145  *      Internal version of timer_sleep that allows
 1146  *      sleeping on a periodic timer.
 1147  */
 1148 kern_return_t timer_sleep_periodic(
 1149         mach_timer_t    timer,
 1150         time_spec_t     expire_time,
 1151         time_spec_t     interval_time,
 1152         thread_t        thread,
 1153         int             flags)
 1154 {
 1155         timer_lock(timer);
 1156 
 1157         assert((timer->tm_misc & TM_ACTIVE) && !(timer->tm_misc & TM_BUSY));
 1158         assert(timer->tm_expire == IP_NULL);
 1159         assert(timer->tm_thread == THREAD_NULL);
 1160 
 1161         timer->tm_thread = thread;
 1162         timer->tm_fcn = timer_expire_synch;
 1163 
 1164         if (flags & TIMER_PERIODIC) {
 1165             timer->tm_period = interval_time;
 1166             timer->tm_flags |= TELT_PERIODIC;
 1167         }
 1168         else {
 1169             timer->tm_flags &= ~TELT_PERIODIC;
 1170         }
 1171 
 1172         timer->tm_misc |= TM_BUSY;
 1173         if (!timer_elt_enqueue(&timer->te,
 1174                                expire_time,
 1175                                (flags & TIMER_ABSOLUTE) != 0))
 1176         {
 1177             /*
 1178              *  Time already passed.  Don`t make thread sleep.
 1179              */
 1180             timer->tm_misc &= ~TM_BUSY;
 1181             timer_unlock(timer);
 1182         }
 1183         else {
 1184             /*
 1185              *  Make thread wait for timer.
 1186              */
 1187             thread_will_wait(thread);
 1188             timer_unlock(timer);
 1189         }
 1190 
 1191         return KERN_SUCCESS;
 1192 }
 1193 
 1194 /*
 1195  *      Change the expiration time or period of a timer
 1196  */
 1197 kern_return_t
 1198 timer_rearm(
 1199         mach_timer_t    timer,
 1200         time_spec_t     new_expire_time,
 1201         time_spec_t     new_interval,
 1202         int             flags)
 1203 {
 1204         timer_lock(timer);
 1205 
 1206         (void) timer_elt_dequeue(&timer->te);
 1207 
 1208         if (flags & TIMER_PERIODIC) {
 1209             timer->tm_period = new_interval;
 1210             timer->tm_flags |= TELT_PERIODIC;
 1211         }
 1212         else {
 1213             timer->tm_flags &= ~TELT_PERIODIC;
 1214         }
 1215 
 1216         timer->tm_flags |= TM_BUSY;
 1217         if (!timer_elt_enqueue(&timer->te,
 1218                                new_expire_time,
 1219                                (flags & TIMER_ABSOLUTE) != 0))
 1220         {
 1221             /*
 1222              *  Time already expired - do action.
 1223              */
 1224             timer_unlock(timer);
 1225             (*timer->tm_fcn)(timer->tm_param);  /* resets 'busy' */
 1226         }
 1227         else {
 1228             timer_unlock(timer);
 1229         }
 1230         return KERN_SUCCESS;
 1231 }
 1232 
 1233 /*
 1234  *      Return the time that the *current* period of a periodic
 1235  *      timer started: expiration_time minus interval.
 1236  *
 1237  *      Returns FALSE if the timer is not queued or not periodic.
 1238  */
 1239 boolean_t timer_start_time(
 1240         mach_timer_t    timer,
 1241         time_spec_t     *start_time)    /* OUT */
 1242 {
 1243         spl_t   s;
 1244 
 1245         /*
 1246          *      Lock the timer, to check whether it is
 1247          *      enqueued.
 1248          */
 1249         timer_lock(timer);
 1250         if (!(timer->tm_misc & TM_ACTIVE) ||
 1251             ((timer->tm_flags & (TELT_SET|TELT_PERIODIC))
 1252                 != (TELT_SET|TELT_PERIODIC)))
 1253         {
 1254             timer_unlock(timer);
 1255             return FALSE;
 1256         }
 1257 
 1258         /*
 1259          *      Lock the clock, to keep the timer from expiring
 1260          *      while we read it.
 1261          */
 1262         s = splsched();
 1263         clock_queue_lock(timer->tm_clock);
 1264 
 1265         /*
 1266          *      The start of the *current* period is
 1267          *      <expire_time> - <interval>.
 1268          */
 1269         *start_time = timer->tm_expire_time;
 1270         time_spec_subtract(*start_time, timer->tm_period);
 1271 
 1272         clock_queue_unlock(timer->tm_clock);
 1273         splx(s);
 1274 
 1275         return TRUE;
 1276 }
 1277 
 1278 
 1279 /*
 1280  *      Make a thread wait for the next expiration of
 1281  *      a periodic timer.
 1282  */
 1283 void timer_wait(
 1284         mach_timer_t    timer,
 1285         thread_t        thread)
 1286 {
 1287         timer_lock(timer);
 1288 
 1289         thread_will_wait(thread);       /* XXX doesn`t need timer! */
 1290 
 1291         timer_unlock(timer);
 1292 }
 1293 
 1294 /*
 1295  *      Handle no_more_senders notification by terminating timer.
 1296  */
 1297 boolean_t
 1298 timer_notify(
 1299         mach_msg_header_t *msg)
 1300 {
 1301         if (msg->msgh_id != MACH_NOTIFY_NO_SENDERS) {
 1302             printf("timer_notify: strange notification %d\n", msg->msgh_id);
 1303             return FALSE;
 1304         }
 1305     {
 1306         mach_no_senders_notification_t *n =
 1307                 (mach_no_senders_notification_t *) msg;
 1308 
 1309         ipc_port_t      port = (ipc_port_t) n->not_header.msgh_remote_port;
 1310         int             ms_count = n->not_count;
 1311 
 1312         mach_timer_t    timer;
 1313 
 1314         /*
 1315          *      Get the timer from the port, and check that
 1316          *      the make-send count is still the same as
 1317          *      the count received in the no-more-senders
 1318          *      notification.
 1319          */
 1320 
 1321         assert(IP_VALID(port));
 1322         timer = convert_port_to_timer(port);
 1323         if (timer == TIMER_NULL)
 1324             return TRUE;        /* message was ours */
 1325 
 1326         timer_lock(timer);
 1327         ip_lock(port);
 1328         if (ip_active(port) &&
 1329             ip_kotype(port) == IKOT_TIMER &&
 1330             port->ip_mscount == ms_count)
 1331         {
 1332             /*
 1333              *  Port is still active, no new send rights have been
 1334              *  created, and there are no internal references from
 1335              *  threads.
 1336              *
 1337              *  Set timer inactive to prevent convert_timer_to_port
 1338              *  from making new send rights.  Then terminate the
 1339              *  timer.
 1340              */
 1341             timer->tm_misc &= ~TM_ACTIVE;
 1342             ip_unlock(port);
 1343 
 1344             timer_terminate_internal(timer);
 1345                 /* timer is now unlocked */
 1346         }
 1347 
 1348         timer_unlock(timer);
 1349 
 1350         /*
 1351          *      Deallocate reference from convert_port_to_timer.
 1352          */
 1353         mach_timer_deallocate(timer);
 1354     }
 1355 
 1356         return TRUE;
 1357 }
 1358 

Cache object: dbacd2de42dcd7f5d06d73a5c2dcf67d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.