blob: 2dc1c2edc6aa0634ffb3c032997504089c1f191e [file] [log] [blame]
/*
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/edp.h>
#include "edp_internal.h"
static inline unsigned int cur_overage(struct edp_client *c)
{
unsigned int cl = cur_level(c);
unsigned int el = e0_level(c);
return cl > el ? cl - el : 0;
}
static inline unsigned int req_overage(struct edp_client *c)
{
unsigned int rl = req_level(c);
unsigned int el = e0_level(c);
return rl > el ? rl - el : 0;
}
/*
* Find the maximum that we can allocate for this client. Since we are
* using a propotional allocation, ensure that the allowed budget is
* fare to other clients. Note that the maximum E-state level is used as
* reference for normalizing client requests (E0 can not be used since
* it could be zer0 for some clients)
*/
static unsigned int approvable_req(struct edp_client *c,
unsigned int net_overage, unsigned int net_max)
{
unsigned int tot_overage;
unsigned int tot_max;
unsigned int fair_level;
unsigned int step;
if (req_index(c) >= c->e0_index)
return req_index(c);
tot_overage = net_overage + c->manager->remaining;
if (cur_overage(c))
tot_overage += cur_overage(c);
else
tot_overage -= e0_level(c) - cur_level(c);
tot_max = net_max + c->states[0];
fair_level = tot_overage * c->states[0] / tot_max + e0_level(c);
step = max(fair_level, cur_level(c) + c->manager->remaining) -
cur_level(c);
return edp_promotion_point(c, step);
}
static void find_net(struct edp_client *client, unsigned int *net_overage,
unsigned int *net_max)
{
struct edp_client *c;
struct edp_manager *m = client->manager;
*net_overage = 0;
*net_max = 0;
list_for_each_entry(c, &m->clients, link) {
if (c != client && cur_level(c) > e0_level(c)) {
*net_overage += cur_overage(c);
*net_max += c->states[0];
}
}
}
static struct edp_client *throttle_pledge(struct edp_client *client,
unsigned int required, unsigned int net_overage,
unsigned int *pledged)
{
struct edp_manager *m = client->manager;
unsigned int deficit = required - m->remaining;
struct edp_client *c;
unsigned int step;
*pledged = m->remaining;
list_for_each_entry_reverse(c, &m->clients, link) {
if (c == client || cur_level(c) <= e0_level(c))
continue;
step = (deficit * cur_overage(c) +
net_overage - 1) / net_overage;
c->gwt = edp_throttling_point(c, step);
*pledged += cur_level(c) - c->states[c->gwt];
if (*pledged >= required)
return c;
}
WARN_ON(*pledged < required);
return c;
}
static void throttle_recover(struct edp_client *client, struct edp_client *tp,
unsigned int required)
{
struct edp_manager *m = client->manager;
unsigned int recovered = m->remaining;
list_for_each_entry_from(tp, &m->clients, link) {
if (tp == client || cur_level(tp) <= e0_level(tp) ||
tp->gwt == cur_index(tp))
continue;
tp->throttle(tp->gwt, tp->private_data);
recovered += cur_level(tp) - tp->states[tp->gwt];
if (tp->cur == tp->req)
m->num_denied++;
tp->cur = tp->states + tp->gwt;
if (recovered >= required)
return;
}
}
static void throttle(struct edp_client *client)
{
struct edp_manager *m = client->manager;
struct edp_client *tp;
unsigned int ar;
unsigned int pledged;
unsigned int required;
unsigned int net_overage;
unsigned int net_max;
find_net(client, &net_overage, &net_max);
ar = approvable_req(client, net_overage, net_max);
required = client->states[ar] - cur_level(client);
if (required <= m->remaining) {
client->cur = client->states + ar;
m->remaining -= required;
return;
}
tp = throttle_pledge(client, required, net_overage, &pledged);
/* E-states are discrete - we may get more than we asked for */
if (pledged > required && ar != req_index(client)) {
ar = edp_promotion_point(client, pledged);
required = client->states[ar] - cur_level(client);
}
throttle_recover(client, tp, required);
client->cur = client->states + ar;
m->remaining = pledged - required;
}
static void overage_update_request(struct edp_client *client,
const unsigned int *req)
{
edp_default_update_request(client, req, throttle);
}
static unsigned int overage_promotion_point(struct edp_client *c,
unsigned int step, unsigned int max)
{
unsigned int lim = cur_level(c) + step;
unsigned int ci = cur_index(c);
unsigned int i = req_index(c);
while (i < ci && c->states[i] > lim)
i++;
/*
* While being throttled, we probably contributed more than our
* fare share - so take the ceiling E-state here
*/
if (c->states[i] < lim && i > req_index(c)) {
if (c->states[i - 1] <= cur_level(c) + max)
i--;
}
return i;
}
static void overage_promote(struct edp_manager *mgr)
{
unsigned int budget = mgr->remaining;
unsigned int net_overage = 0;
struct edp_client *c;
unsigned int step;
unsigned int pp;
list_for_each_entry(c, &mgr->clients, link) {
if (req_level(c) > cur_level(c) && c->notify_promotion)
net_overage += req_overage(c);
}
/* Guarding against division-by-zero */
if (!net_overage) {
WARN_ON(1);
return;
}
list_for_each_entry(c, &mgr->clients, link) {
if (req_level(c) <= cur_level(c) || !c->notify_promotion)
continue;
step = (req_overage(c) * budget +
net_overage - 1) / net_overage;
if (step > mgr->remaining)
step = mgr->remaining;
pp = overage_promotion_point(c, step, mgr->remaining);
if (pp == cur_index(c))
continue;
mgr->remaining -= c->states[pp] - cur_level(c);
c->cur = c->states + pp;
if (c->cur == c->req)
mgr->num_denied--;
c->notify_promotion(pp, c->private_data);
if (!mgr->remaining || !mgr->num_denied)
return;
}
}
static struct edp_governor overage_governor = {
.name = "overage",
.owner = THIS_MODULE,
.update_request = overage_update_request,
.update_loans = edp_default_update_loans,
.promote = overage_promote
};
static int __init overage_init(void)
{
return edp_register_governor(&overage_governor);
}
postcore_initcall(overage_init);