1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2017, Jeffrey Roberson <jeff@freebsd.org> 5 * All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifndef _SYS_PIDCTRL_H_ 30 #define _SYS_PIDCTRL_H_ 31 32 /* 33 * Proportional Integral Derivative controller. 34 * 35 * This controller is intended to replace a multitude of threshold based 36 * daemon regulation systems. These systems produce sharp sawtooths of 37 * activity which can cause latency spikes and other undesireable bursty 38 * behavior. The PID controller adapts to changing load conditions and 39 * adjusts the work done by the daemon to keep a smoother output. 40 * 41 * The setpoint can be thought of as a single watermark that the controller 42 * is always trying to reach. Compared to a high water/low water type 43 * algorithm the pid controller is dynamically deciding the low water and 44 * regulating to the high water. The setpoint should be high enough that 45 * the controller and daemon have time to observe the rise in value and 46 * respond to it, else the resource may be exhausted. More frequent wakeups 47 * permit higher setpoints and less underutilized resources. 48 * 49 * The controller has been optimised for simplicity of math making it quite 50 * inexpensive to execute. There is no floating point and so the gains must 51 * be the inverse of whole integers. 52 * 53 * Failing to measure and tune the gain parameters can result in wild 54 * oscillations in output. It is strongly encouraged that controllers are 55 * tested and tuned under a wide variety of workloads before gain values are 56 * picked. Some reasonable defaults are provided below. 57 */ 58 59 struct pidctrl { 60 /* Saved control variables. */ 61 int pc_error; /* Current error. */ 62 int pc_olderror; /* Saved error for derivative. */ 63 int pc_integral; /* Integral accumulator. */ 64 int pc_derivative; /* Change from last error. */ 65 int pc_input; /* Last input. */ 66 int pc_output; /* Last output. */ 67 int pc_ticks; /* Last sampling time. */ 68 /* configuration options, runtime tunable via sysctl */ 69 int pc_setpoint; /* Desired level */ 70 int pc_interval; /* Update interval in ticks. */ 71 int pc_bound; /* Integral wind-up limit. */ 72 int pc_Kpd; /* Proportional gain divisor. */ 73 int pc_Kid; /* Integral gain divisor. */ 74 int pc_Kdd; /* Derivative gain divisor. */ 75 }; 76 77 /* 78 * Reasonable default divisors. 79 * 80 * Actual gains are 1/divisor. Gains interact in complex ways with the 81 * setpoint and interval. Measurement under multiple loads should be 82 * taken to ensure adequate stability and rise time. 83 */ 84 #define PIDCTRL_KPD 3 /* Default proportional divisor. */ 85 #define PIDCTRL_KID 4 /* Default integral divisor. */ 86 #define PIDCTRL_KDD 8 /* Default derivative divisor. */ 87 #define PIDCTRL_BOUND 4 /* Bound factor, setpoint multiple. */ 88 89 struct sysctl_oid_list; 90 91 void pidctrl_init(struct pidctrl *pc, int interval, int setpoint, 92 int bound, int Kpd, int Kid, int Kdd); 93 void pidctrl_init_sysctl(struct pidctrl *pc, struct sysctl_oid_list *parent); 94 95 /* 96 * This is the classic PID controller where the interval is clamped to 97 * [-bound, bound] and the output may be negative. This should be used 98 * in continuous control loops that can adjust a process variable in 99 * either direction. This is a descrete time controller and should 100 * only be called once per-interval or the derivative term will be 101 * inaccurate. 102 */ 103 int pidctrl_classic(struct pidctrl *pc, int input); 104 105 /* 106 * This controler is intended for consumer type daemons that can only 107 * regulate in a positive direction, that is to say, they can not exert 108 * positive pressure on the process variable or input. They can only 109 * reduce it by doing work. As such the integral is bound between [0, bound] 110 * and the output is similarly a positive value reflecting the units of 111 * work necessary to be completed in the current interval to eliminate error. 112 * 113 * It is a descrete time controller but can be invoked more than once in a 114 * given time interval for ease of client implementation. This should only 115 * be done in overload situations or the controller may not produce a stable 116 * output. Calling it less frequently when there is no work to be done will 117 * increase the rise time but should otherwise be harmless. 118 */ 119 int pidctrl_daemon(struct pidctrl *pc, int input); 120 121 #endif /* !_SYS_PIDCTRL_H_ */ 122