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/kernel/latency.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  * latency.c: Explicit system-wide latency-expectation infrastructure
    3  *
    4  * The purpose of this infrastructure is to allow device drivers to set
    5  * latency constraint they have and to collect and summarize these
    6  * expectations globally. The cummulated result can then be used by
    7  * power management and similar users to make decisions that have
    8  * tradoffs with a latency component.
    9  *
   10  * An example user of this are the x86 C-states; each higher C state saves
   11  * more power, but has a higher exit latency. For the idle loop power
   12  * code to make a good decision which C-state to use, information about
   13  * acceptable latencies is required.
   14  *
   15  * An example announcer of latency is an audio driver that knowns it
   16  * will get an interrupt when the hardware has 200 usec of samples
   17  * left in the DMA buffer; in that case the driver can set a latency
   18  * constraint of, say, 150 usec.
   19  *
   20  * Multiple drivers can each announce their maximum accepted latency,
   21  * to keep these appart, a string based identifier is used.
   22  *
   23  *
   24  * (C) Copyright 2006 Intel Corporation
   25  * Author: Arjan van de Ven <arjan@linux.intel.com>
   26  *
   27  * This program is free software; you can redistribute it and/or
   28  * modify it under the terms of the GNU General Public License
   29  * as published by the Free Software Foundation; version 2
   30  * of the License.
   31  */
   32 
   33 #include <linux/latency.h>
   34 #include <linux/list.h>
   35 #include <linux/spinlock.h>
   36 #include <linux/slab.h>
   37 #include <linux/module.h>
   38 #include <linux/notifier.h>
   39 #include <linux/jiffies.h>
   40 #include <asm/atomic.h>
   41 
   42 struct latency_info {
   43         struct list_head list;
   44         int usecs;
   45         char *identifier;
   46 };
   47 
   48 /*
   49  * locking rule: all modifications to current_max_latency and
   50  * latency_list need to be done while holding the latency_lock.
   51  * latency_lock needs to be taken _irqsave.
   52  */
   53 static atomic_t current_max_latency;
   54 static DEFINE_SPINLOCK(latency_lock);
   55 
   56 static LIST_HEAD(latency_list);
   57 static BLOCKING_NOTIFIER_HEAD(latency_notifier);
   58 
   59 /*
   60  * This function returns the maximum latency allowed, which
   61  * happens to be the minimum of all maximum latencies on the
   62  * list.
   63  */
   64 static int __find_max_latency(void)
   65 {
   66         int min = INFINITE_LATENCY;
   67         struct latency_info *info;
   68 
   69         list_for_each_entry(info, &latency_list, list) {
   70                 if (info->usecs < min)
   71                         min = info->usecs;
   72         }
   73         return min;
   74 }
   75 
   76 /**
   77  * set_acceptable_latency - sets the maximum latency acceptable
   78  * @identifier: string that identifies this driver
   79  * @usecs: maximum acceptable latency for this driver
   80  *
   81  * This function informs the kernel that this device(driver)
   82  * can accept at most usecs latency. This setting is used for
   83  * power management and similar tradeoffs.
   84  *
   85  * This function sleeps and can only be called from process
   86  * context.
   87  * Calling this function with an existing identifier is valid
   88  * and will cause the existing latency setting to be changed.
   89  */
   90 void set_acceptable_latency(char *identifier, int usecs)
   91 {
   92         struct latency_info *info, *iter;
   93         unsigned long flags;
   94         int found_old = 0;
   95 
   96         info = kzalloc(sizeof(struct latency_info), GFP_KERNEL);
   97         if (!info)
   98                 return;
   99         info->usecs = usecs;
  100         info->identifier = kstrdup(identifier, GFP_KERNEL);
  101         if (!info->identifier)
  102                 goto free_info;
  103 
  104         spin_lock_irqsave(&latency_lock, flags);
  105         list_for_each_entry(iter, &latency_list, list) {
  106                 if (strcmp(iter->identifier, identifier)==0) {
  107                         found_old = 1;
  108                         iter->usecs = usecs;
  109                         break;
  110                 }
  111         }
  112         if (!found_old)
  113                 list_add(&info->list, &latency_list);
  114 
  115         if (usecs < atomic_read(&current_max_latency))
  116                 atomic_set(&current_max_latency, usecs);
  117 
  118         spin_unlock_irqrestore(&latency_lock, flags);
  119 
  120         blocking_notifier_call_chain(&latency_notifier,
  121                 atomic_read(&current_max_latency), NULL);
  122 
  123         /*
  124          * if we inserted the new one, we're done; otherwise there was
  125          * an existing one so we need to free the redundant data
  126          */
  127         if (!found_old)
  128                 return;
  129 
  130         kfree(info->identifier);
  131 free_info:
  132         kfree(info);
  133 }
  134 EXPORT_SYMBOL_GPL(set_acceptable_latency);
  135 
  136 /**
  137  * modify_acceptable_latency - changes the maximum latency acceptable
  138  * @identifier: string that identifies this driver
  139  * @usecs: maximum acceptable latency for this driver
  140  *
  141  * This function informs the kernel that this device(driver)
  142  * can accept at most usecs latency. This setting is used for
  143  * power management and similar tradeoffs.
  144  *
  145  * This function does not sleep and can be called in any context.
  146  * Trying to use a non-existing identifier silently gets ignored.
  147  *
  148  * Due to the atomic nature of this function, the modified latency
  149  * value will only be used for future decisions; past decisions
  150  * can still lead to longer latencies in the near future.
  151  */
  152 void modify_acceptable_latency(char *identifier, int usecs)
  153 {
  154         struct latency_info *iter;
  155         unsigned long flags;
  156 
  157         spin_lock_irqsave(&latency_lock, flags);
  158         list_for_each_entry(iter, &latency_list, list) {
  159                 if (strcmp(iter->identifier, identifier) == 0) {
  160                         iter->usecs = usecs;
  161                         break;
  162                 }
  163         }
  164         if (usecs < atomic_read(&current_max_latency))
  165                 atomic_set(&current_max_latency, usecs);
  166         spin_unlock_irqrestore(&latency_lock, flags);
  167 }
  168 EXPORT_SYMBOL_GPL(modify_acceptable_latency);
  169 
  170 /**
  171  * remove_acceptable_latency - removes the maximum latency acceptable
  172  * @identifier: string that identifies this driver
  173  *
  174  * This function removes a previously set maximum latency setting
  175  * for the driver and frees up any resources associated with the
  176  * bookkeeping needed for this.
  177  *
  178  * This function does not sleep and can be called in any context.
  179  * Trying to use a non-existing identifier silently gets ignored.
  180  */
  181 void remove_acceptable_latency(char *identifier)
  182 {
  183         unsigned long flags;
  184         int newmax = 0;
  185         struct latency_info *iter, *temp;
  186 
  187         spin_lock_irqsave(&latency_lock, flags);
  188 
  189         list_for_each_entry_safe(iter,  temp, &latency_list, list) {
  190                 if (strcmp(iter->identifier, identifier) == 0) {
  191                         list_del(&iter->list);
  192                         newmax = iter->usecs;
  193                         kfree(iter->identifier);
  194                         kfree(iter);
  195                         break;
  196                 }
  197         }
  198 
  199         /* If we just deleted the system wide value, we need to
  200          * recalculate with a full search
  201          */
  202         if (newmax == atomic_read(&current_max_latency)) {
  203                 newmax = __find_max_latency();
  204                 atomic_set(&current_max_latency, newmax);
  205         }
  206         spin_unlock_irqrestore(&latency_lock, flags);
  207 }
  208 EXPORT_SYMBOL_GPL(remove_acceptable_latency);
  209 
  210 /**
  211  * system_latency_constraint - queries the system wide latency maximum
  212  *
  213  * This function returns the system wide maximum latency in
  214  * microseconds.
  215  *
  216  * This function does not sleep and can be called in any context.
  217  */
  218 int system_latency_constraint(void)
  219 {
  220         return atomic_read(&current_max_latency);
  221 }
  222 EXPORT_SYMBOL_GPL(system_latency_constraint);
  223 
  224 /**
  225  * synchronize_acceptable_latency - recalculates all latency decisions
  226  *
  227  * This function will cause a callback to various kernel pieces that
  228  * will make those pieces rethink their latency decisions. This implies
  229  * that if there are overlong latencies in hardware state already, those
  230  * latencies get taken right now. When this call completes no overlong
  231  * latency decisions should be active anymore.
  232  *
  233  * Typical usecase of this is after a modify_acceptable_latency() call,
  234  * which in itself is non-blocking and non-synchronizing.
  235  *
  236  * This function blocks and should not be called with locks held.
  237  */
  238 
  239 void synchronize_acceptable_latency(void)
  240 {
  241         blocking_notifier_call_chain(&latency_notifier,
  242                 atomic_read(&current_max_latency), NULL);
  243 }
  244 EXPORT_SYMBOL_GPL(synchronize_acceptable_latency);
  245 
  246 /*
  247  * Latency notifier: this notifier gets called when a non-atomic new
  248  * latency value gets set. The expectation nof the caller of the
  249  * non-atomic set is that when the call returns, future latencies
  250  * are within bounds, so the functions on the notifier list are
  251  * expected to take the overlong latencies immediately, inside the
  252  * callback, and not make a overlong latency decision anymore.
  253  *
  254  * The callback gets called when the new latency value is made
  255  * active so system_latency_constraint() returns the new latency.
  256  */
  257 int register_latency_notifier(struct notifier_block * nb)
  258 {
  259         return blocking_notifier_chain_register(&latency_notifier, nb);
  260 }
  261 EXPORT_SYMBOL_GPL(register_latency_notifier);
  262 
  263 int unregister_latency_notifier(struct notifier_block * nb)
  264 {
  265         return blocking_notifier_chain_unregister(&latency_notifier, nb);
  266 }
  267 EXPORT_SYMBOL_GPL(unregister_latency_notifier);
  268 
  269 static __init int latency_init(void)
  270 {
  271         atomic_set(&current_max_latency, INFINITE_LATENCY);
  272         /*
  273          * we don't want by default to have longer latencies than 2 ticks,
  274          * since that would cause lost ticks
  275          */
  276         set_acceptable_latency("kernel", 2*1000000/HZ);
  277         return 0;
  278 }
  279 
  280 module_init(latency_init);

Cache object: 5bf9db86774e7ee8846b3118ae9ecc1f


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