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/kern_cputimer.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  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
    3  * 
    4  * This code is derived from software contributed to The DragonFly Project
    5  * by Matthew Dillon <dillon@backplane.com>
    6  * 
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in
   15  *    the documentation and/or other materials provided with the
   16  *    distribution.
   17  * 3. Neither the name of The DragonFly Project nor the names of its
   18  *    contributors may be used to endorse or promote products derived
   19  *    from this software without specific, prior written permission.
   20  * 
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
   25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   32  * SUCH DAMAGE.
   33  * 
   34  * $DragonFly: src/sys/kern/kern_cputimer.c,v 1.5 2008/06/05 18:06:32 swildner Exp $
   35  */
   36 /*
   37  * Generic cputimer - access to a reliable, free-running counter.
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/kernel.h>
   42 #include <sys/systm.h>
   43 #include <sys/thread.h>
   44 #include <sys/globaldata.h>
   45 #include <sys/systimer.h>
   46 #include <sys/sysctl.h>
   47 #include <sys/thread2.h>
   48 
   49 static sysclock_t dummy_cputimer_count(void);
   50 
   51 static struct cputimer dummy_cputimer = {
   52     SLIST_ENTRY_INITIALIZER,
   53     "dummy",
   54     CPUTIMER_PRI_DUMMY,
   55     CPUTIMER_DUMMY,
   56     dummy_cputimer_count,
   57     cputimer_default_fromhz,
   58     cputimer_default_fromus,
   59     cputimer_default_construct,
   60     cputimer_default_destruct,
   61     1000000,
   62     (1000000LL << 32) / 1000000,
   63     (1000000000LL << 32) / 1000000,
   64     0
   65 };
   66 
   67 struct cputimer *sys_cputimer = &dummy_cputimer;
   68 SLIST_HEAD(, cputimer) cputimerhead = SLIST_HEAD_INITIALIZER(&cputimerhead);
   69 
   70 /*
   71  * Generic cputimer API
   72  */
   73 void
   74 cputimer_select(struct cputimer *timer, int pri)
   75 {
   76     sysclock_t oldclock;
   77 
   78     /*
   79      * Calculate helper fields
   80      */
   81     cputimer_set_frequency(timer, timer->freq);
   82 
   83     /*
   84      * Install a new cputimer if its priority allows it.  If timer is
   85      * passed as NULL we deinstall the current timer and revert to our
   86      * dummy.
   87      */
   88     if (pri == 0)
   89         pri = timer->pri;
   90     if (timer == NULL || pri >= sys_cputimer->pri) {
   91         oldclock = sys_cputimer->count();
   92         sys_cputimer->destruct(sys_cputimer);
   93         sys_cputimer = &dummy_cputimer;
   94         if (timer) {
   95             sys_cputimer = timer;
   96             timer->construct(timer, oldclock);
   97             cputimer_intr_config(timer);
   98         }
   99     }
  100 }
  101 
  102 /*
  103  * Register a timer.  If the timer has already been registered, do nothing.
  104  */
  105 void
  106 cputimer_register(struct cputimer *timer)
  107 {
  108     struct cputimer *scan;
  109 
  110     /*
  111      * Initialize dummy_cputimer if the slist is empty, it does not get
  112      * registered the normal way.
  113      */
  114     if (SLIST_EMPTY(&cputimerhead))
  115         SLIST_FIRST(&cputimerhead) = &dummy_cputimer;
  116     SLIST_FOREACH(scan, &cputimerhead, next) {
  117         if (scan == timer)
  118             return;
  119     }
  120     SLIST_INSERT_HEAD(&cputimerhead, timer, next);
  121 }
  122 
  123 /*
  124  * Deregister a timer.  If the timer has already been deregistered, do nothing.
  125  */
  126 void
  127 cputimer_deregister(struct cputimer *timer)
  128 {
  129     struct cputimer *scan;
  130     struct cputimer *best;
  131 
  132     /*
  133      * Locate and remove the timer.  If the timer is our currently active
  134      * timer, revert to the dummy timer.
  135      */
  136     SLIST_FOREACH(scan, &cputimerhead, next) {
  137             if (timer == scan) {
  138                 if (timer == sys_cputimer)
  139                     cputimer_select(&dummy_cputimer, 0x7FFFFFFF);
  140                 SLIST_REMOVE(&cputimerhead, timer, cputimer, next);
  141                 break;
  142             }
  143     }
  144 
  145     /*
  146      * If sys_cputimer reverted to the dummy, select the best one
  147      */
  148     if (sys_cputimer == &dummy_cputimer) {
  149         best = NULL;
  150         SLIST_FOREACH(scan, &cputimerhead, next) {
  151             if (best == NULL || scan->pri > best->pri)
  152                 best = scan;
  153         }
  154         if (best)
  155             cputimer_select(best, 0x7FFFFFFF);
  156     }
  157 }
  158 
  159 /*
  160  * Calculate usec / tick and nsec / tick, scaled by (1 << 32).
  161  *
  162  * so e.g. a 3 mhz timer would be 3 usec / tick x (1 << 32),
  163  * or 3000 nsec / tick x (1 << 32)
  164  */
  165 void
  166 cputimer_set_frequency(struct cputimer *timer, sysclock_t freq)
  167 {
  168     timer->freq = freq;
  169     timer->freq64_usec = (1000000LL << 32) / freq;
  170     timer->freq64_nsec = (1000000000LL << 32) / freq;
  171     if (timer == sys_cputimer)
  172         cputimer_intr_config(timer);
  173 }
  174 
  175 sysclock_t
  176 cputimer_default_fromhz(int freq)
  177 {
  178     return(sys_cputimer->freq / freq + 1);
  179 }
  180 
  181 sysclock_t
  182 cputimer_default_fromus(int us)
  183 {
  184     return((int64_t)sys_cputimer->freq * us / 1000000);
  185 }
  186 
  187 /*
  188  * Dummy counter implementation
  189  */
  190 static
  191 sysclock_t
  192 dummy_cputimer_count(void)
  193 {
  194     return(++dummy_cputimer.base);
  195 }
  196 
  197 void
  198 cputimer_default_construct(struct cputimer *cputimer, sysclock_t oldclock)
  199 {
  200     cputimer->base = oldclock;
  201 }
  202 
  203 void
  204 cputimer_default_destruct(struct cputimer *cputimer)
  205 {
  206 }
  207 
  208 /************************************************************************
  209  *                              SYSCTL SUPPORT                          *
  210  ************************************************************************
  211  *
  212  * Note: the ability to change the systimer is not currently enabled
  213  * because it will mess up systimer calculations.  You have to live
  214  * with what is configured at boot.
  215  */
  216 static int
  217 sysctl_cputimer_reglist(SYSCTL_HANDLER_ARGS)
  218 {
  219     struct cputimer *scan;
  220     int error = 0;
  221     int loop = 0;
  222 
  223     /*
  224      * Build a list of available timers
  225      */
  226     SLIST_FOREACH(scan, &cputimerhead, next) {
  227         if (error == 0 && loop)
  228             error = SYSCTL_OUT(req, " ", 1);
  229         if (error == 0)
  230             error = SYSCTL_OUT(req, scan->name, strlen(scan->name));
  231         ++loop;
  232     }
  233     return (error);
  234 }
  235 
  236 static int
  237 sysctl_cputimer_name(SYSCTL_HANDLER_ARGS)
  238 {
  239     int error;
  240 
  241     error = SYSCTL_OUT(req, sys_cputimer->name, strlen(sys_cputimer->name));
  242     return (error);
  243 }
  244 
  245 static int
  246 sysctl_cputimer_clock(SYSCTL_HANDLER_ARGS)
  247 {
  248     sysclock_t clock;
  249     int error;
  250 
  251     clock = sys_cputimer->count();
  252     error = SYSCTL_OUT(req, &clock, sizeof(clock));
  253     return (error);
  254 }
  255 
  256 static int
  257 sysctl_cputimer_freq(SYSCTL_HANDLER_ARGS)
  258 {
  259     int error;
  260 
  261     error = SYSCTL_OUT(req, &sys_cputimer->freq, sizeof(sys_cputimer->freq));
  262     return (error);
  263 }
  264 
  265 SYSCTL_DECL(_kern_cputimer);
  266 SYSCTL_NODE(_kern, OID_AUTO, cputimer, CTLFLAG_RW, NULL, "cputimer");
  267 
  268 SYSCTL_PROC(_kern_cputimer, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RD,
  269             NULL, 0, sysctl_cputimer_reglist, "A", "");
  270 SYSCTL_PROC(_kern_cputimer, OID_AUTO, name, CTLTYPE_STRING|CTLFLAG_RD,
  271             NULL, 0, sysctl_cputimer_name, "A", "");
  272 SYSCTL_PROC(_kern_cputimer, OID_AUTO, clock, CTLTYPE_UINT|CTLFLAG_RD,
  273             NULL, 0, sysctl_cputimer_clock, "IU", "");
  274 SYSCTL_PROC(_kern_cputimer, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD,
  275             NULL, 0, sysctl_cputimer_freq, "I", "");
  276 
  277 static struct cputimer_intr *sys_cputimer_intr;
  278 static uint32_t cputimer_intr_caps;
  279 SLIST_HEAD(, cputimer_intr) cputimer_intr_head =
  280         SLIST_HEAD_INITIALIZER(&cputimer_intr_head);
  281 
  282 void
  283 cputimer_intr_register(struct cputimer_intr *cti)
  284 {
  285     struct cputimer_intr *scan;
  286 
  287     SLIST_FOREACH(scan, &cputimer_intr_head, next) {
  288         if (scan == cti)
  289             return;
  290     }
  291     cti->config(cti, sys_cputimer);
  292     SLIST_INSERT_HEAD(&cputimer_intr_head, cti, next);
  293 }
  294 
  295 void
  296 cputimer_intr_deregister(struct cputimer_intr *cti)
  297 {
  298     KKASSERT(cti != sys_cputimer_intr);
  299     SLIST_REMOVE(&cputimer_intr_head, cti, cputimer_intr, next);
  300 }
  301 
  302 int
  303 cputimer_intr_select(struct cputimer_intr *cti, int prio)
  304 {
  305     KKASSERT(cti != NULL);
  306 
  307     if (prio == 0)
  308         prio = cti->prio;
  309 
  310     if (sys_cputimer_intr == NULL) {
  311         KKASSERT(cputimer_intr_caps == 0);
  312         sys_cputimer_intr = cti;
  313         return 0;
  314     }
  315 
  316     if ((cti->caps & cputimer_intr_caps) == cputimer_intr_caps) {
  317         if (prio > sys_cputimer_intr->prio) {
  318             sys_cputimer_intr = cti;
  319             return 0;
  320         } else {
  321             return EBUSY;
  322         }
  323     } else {
  324         return EOPNOTSUPP;
  325     }
  326 }
  327 
  328 void
  329 cputimer_intr_default_enable(struct cputimer_intr *cti __unused)
  330 {
  331 }
  332 
  333 void
  334 cputimer_intr_default_restart(struct cputimer_intr *cti)
  335 {
  336     cti->reload(cti, 0);
  337 }
  338 
  339 void
  340 cputimer_intr_default_config(struct cputimer_intr *cti __unused,
  341                              const struct cputimer *timer __unused)
  342 {
  343 }
  344 
  345 void
  346 cputimer_intr_default_pmfixup(struct cputimer_intr *cti __unused)
  347 {
  348 }
  349 
  350 void
  351 cputimer_intr_default_initclock(struct cputimer_intr *cti __unused,
  352                                 boolean_t selected __unused)
  353 {
  354 }
  355 
  356 void
  357 cputimer_intr_enable(void)
  358 {
  359     struct cputimer_intr *cti;
  360 
  361     SLIST_FOREACH(cti, &cputimer_intr_head, next)
  362         cti->enable(cti);
  363 }
  364 
  365 void
  366 cputimer_intr_config(const struct cputimer *timer)
  367 {
  368     struct cputimer_intr *cti;
  369 
  370     SLIST_FOREACH(cti, &cputimer_intr_head, next)
  371         cti->config(cti, timer);
  372 }
  373 
  374 void
  375 cputimer_intr_pmfixup(void)
  376 {
  377     struct cputimer_intr *cti;
  378 
  379     SLIST_FOREACH(cti, &cputimer_intr_head, next)
  380         cti->pmfixup(cti);
  381 }
  382 
  383 void
  384 cputimer_intr_reload(sysclock_t reload)
  385 {
  386     struct cputimer_intr *cti = sys_cputimer_intr;
  387 
  388     cti->reload(cti, reload);
  389 }
  390 
  391 void
  392 cputimer_intr_restart(void)
  393 {
  394     struct cputimer_intr *cti = sys_cputimer_intr;
  395 
  396     cti->restart(cti);
  397 }
  398 
  399 int
  400 cputimer_intr_select_caps(uint32_t caps)
  401 {
  402     struct cputimer_intr *cti, *maybe;
  403     int error;
  404 
  405     maybe = NULL;
  406     SLIST_FOREACH(cti, &cputimer_intr_head, next) {
  407         if ((cti->caps & caps) == caps) {
  408             if (maybe == NULL)
  409                 maybe = cti;
  410             else if (cti->prio > maybe->prio)
  411                 maybe = cti;
  412         }
  413     }
  414     if (maybe == NULL)
  415         return ENOENT;
  416 
  417     cputimer_intr_caps = caps;
  418     error = cputimer_intr_select(maybe, CPUTIMER_INTR_PRIO_MAX);
  419     KKASSERT(!error);
  420 
  421     return 0;
  422 }
  423 
  424 static void
  425 cputimer_intr_initclocks(void)
  426 {
  427     struct cputimer_intr *cti, *ncti;
  428 
  429     /*
  430      * An interrupt cputimer may deregister itself,
  431      * so use SLIST_FOREACH_MUTABLE here.
  432      */
  433     SLIST_FOREACH_MUTABLE(cti, &cputimer_intr_head, next, ncti) {
  434         boolean_t selected = FALSE;
  435 
  436         if (cti == sys_cputimer_intr)
  437             selected = TRUE;
  438         cti->initclock(cti, selected);
  439     }
  440 }
  441 /* NOTE: Must be SECOND to allow platform initialization to go first */
  442 SYSINIT(cputimer_intr, SI_BOOT2_CLOCKREG, SI_ORDER_SECOND,
  443         cputimer_intr_initclocks, NULL)
  444 
  445 static int
  446 sysctl_cputimer_intr_reglist(SYSCTL_HANDLER_ARGS)
  447 {
  448     struct cputimer_intr *scan;
  449     int error = 0;
  450     int loop = 0;
  451 
  452     /*
  453      * Build a list of available interrupt cputimers
  454      */
  455     SLIST_FOREACH(scan, &cputimer_intr_head, next) {
  456         if (error == 0 && loop)
  457             error = SYSCTL_OUT(req, " ", 1);
  458         if (error == 0)
  459             error = SYSCTL_OUT(req, scan->name, strlen(scan->name));
  460         ++loop;
  461     }
  462     return (error);
  463 }
  464 
  465 static int
  466 sysctl_cputimer_intr_freq(SYSCTL_HANDLER_ARGS)
  467 {
  468     int error;
  469 
  470     error = SYSCTL_OUT(req, &sys_cputimer_intr->freq,
  471                        sizeof(sys_cputimer_intr->freq));
  472     return (error);
  473 }
  474 
  475 static int
  476 sysctl_cputimer_intr_select(SYSCTL_HANDLER_ARGS)
  477 {
  478     struct cputimer_intr *cti;
  479     char name[32];
  480     int error;
  481 
  482     ksnprintf(name, sizeof(name), "%s", sys_cputimer_intr->name);
  483     error = sysctl_handle_string(oidp, name, sizeof(name), req);
  484     if (error != 0 || req->newptr == NULL)
  485         return error;
  486 
  487     SLIST_FOREACH(cti, &cputimer_intr_head, next) {
  488         if (strcmp(cti->name, name) == 0)
  489             break;
  490     }
  491     if (cti == NULL)
  492         return ENOENT;
  493     if (cti == sys_cputimer_intr)
  494         return 0;
  495 
  496     error = cputimer_intr_select(cti, CPUTIMER_INTR_PRIO_MAX);
  497     if (!error)
  498         cputimer_intr_restart();
  499     return error;
  500 }
  501 
  502 SYSCTL_NODE(_kern_cputimer, OID_AUTO, intr, CTLFLAG_RW, NULL,
  503             "interrupt cputimer");
  504 
  505 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, reglist, CTLTYPE_STRING|CTLFLAG_RD,
  506             NULL, 0, sysctl_cputimer_intr_reglist, "A", "");
  507 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD,
  508             NULL, 0, sysctl_cputimer_intr_freq, "I", "");
  509 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RW,
  510             NULL, 0, sysctl_cputimer_intr_select, "A", "");

Cache object: 849745ae50ac07be1f429eb87c4ed628


[ 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.