master
  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
 59struct 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
 89struct sysctl_oid_list;
 90
 91void	pidctrl_init(struct pidctrl *pc, int interval, int setpoint,
 92	    int bound, int Kpd, int Kid, int Kdd);
 93void	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 */
103int	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 */
119int	pidctrl_daemon(struct pidctrl *pc, int input);
120
121#endif	/* !_SYS_PIDCTRL_H_ */