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