freebsd-dev/sys/sys/pidctrl.h
Jeff Roberson 5f8cd1c0bf Add a generic Proportional Integral Derivative (PID) controller algorithm and
use it to regulate page daemon output.

This provides much smoother and more responsive page daemon output, anticipating
demand and avoiding pageout stalls by increasing the number of pages to match
the workload.  This is a reimplementation of work done by myself and mlaier at
Isilon.

Reviewed by:	bsdimp
Tested by:	pho
Sponsored by:	Netflix, Dell/EMC Isilon
Differential Revision:	https://reviews.freebsd.org/D14402
2018-02-23 22:51:51 +00:00

124 lines
5.4 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2017, Jeffrey Roberson <jeff@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _SYS_PIDCTRL_H_
#define _SYS_PIDCTRL_H_
/*
* Proportional Integral Derivative controller.
*
* This controller is intended to replace a multitude of threshold based
* daemon regulation systems. These systems produce sharp sawtooths of
* activity which can cause latency spikes and other undesireable bursty
* behavior. The PID controller adapts to changing load conditions and
* adjusts the work done by the daemon to keep a smoother output.
*
* The setpoint can be thought of as a single watermark that the controller
* is always trying to reach. Compared to a high water/low water type
* algorithm the pid controller is dynamically deciding the low water and
* regulating to the high water. The setpoint should be high enough that
* the controller and daemon have time to observe the rise in value and
* respond to it, else the resource may be exhausted. More frequent wakeups
* permit higher setpoints and less underutilized resources.
*
* The controller has been optimised for simplicity of math making it quite
* inexpensive to execute. There is no floating point and so the gains must
* be the inverse of whole integers.
*
* Failing to measure and tune the gain parameters can result in wild
* oscillations in output. It is strongly encouraged that controllers are
* tested and tuned under a wide variety of workloads before gain values are
* picked. Some reasonable defaults are provided below.
*/
struct pidctrl {
/* Saved control variables. */
int pc_error; /* Current error. */
int pc_olderror; /* Saved error for derivative. */
int pc_integral; /* Integral accumulator. */
int pc_derivative; /* Change from last error. */
int pc_input; /* Last input. */
int pc_output; /* Last output. */
int pc_ticks; /* Last sampling time. */
/* configuration options, runtime tunable via sysctl */
int pc_setpoint; /* Desired level */
int pc_interval; /* Update interval in ticks. */
int pc_bound; /* Integral wind-up limit. */
int pc_Kpd; /* Proportional gain divisor. */
int pc_Kid; /* Integral gain divisor. */
int pc_Kdd; /* Derivative gain divisor. */
};
/*
* Reasonable default divisors.
*
* Actual gains are 1/divisor. Gains interact in complex ways with the
* setpoint and interval. Measurement under multiple loads should be
* taken to ensure adequate stability and rise time.
*/
#define PIDCTRL_KPD 3 /* Default proportional divisor. */
#define PIDCTRL_KID 4 /* Default integral divisor. */
#define PIDCTRL_KDD 8 /* Default derivative divisor. */
#define PIDCTRL_BOUND 4 /* Bound factor, setpoint multiple. */
struct sysctl_oid_list;
void pidctrl_init(struct pidctrl *pc, int interval, int setpoint,
int bound, int Kpd, int Kid, int Kdd);
void pidctrl_init_sysctl(struct pidctrl *pc, struct sysctl_oid_list *parent);
/*
* This is the classic PID controller where the interval is clamped to
* [-bound, bound] and the output may be negative. This should be used
* in continuous control loops that can adjust a process variable in
* either direction. This is a descrete time controller and should
* only be called once per-interval or the derivative term will be
* inaccurate.
*/
int pidctrl_classic(struct pidctrl *pc, int input);
/*
* This controler is intended for consumer type daemons that can only
* regulate in a positive direction, that is to say, they can not exert
* positive pressure on the process variable or input. They can only
* reduce it by doing work. As such the integral is bound between [0, bound]
* and the output is similarly a positive value reflecting the units of
* work necessary to be completed in the current interval to eliminate error.
*
* It is a descrete time controller but can be invoked more than once in a
* given time interval for ease of client implementation. This should only
* be done in overload situations or the controller may not produce a stable
* output. Calling it less frequently when there is no work to be done will
* increase the rise time but should otherwise be harmless.
*/
int pidctrl_daemon(struct pidctrl *pc, int input);
#endif /* !_SYS_PIDCTRL_H_ */