/* * 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 . */ #include #include #include #include "edp_internal.h" static unsigned int approvable_req(struct edp_client *c, unsigned int net) { unsigned int fair_level; unsigned int step; unsigned int cl; if (req_index(c) >= c->e0_index) return req_index(c); cl = cur_level(c); fair_level = c->manager->max * e0_level(c) / net; step = max(fair_level, cl + c->manager->remaining) - cl; return edp_promotion_point(c, step); } static unsigned int net_e0(struct edp_client *client) { struct edp_client *c; unsigned int net = 0; list_for_each_entry(c, &client->manager->clients, link) net += e0_level(c); return net; } static struct edp_client *throttle_pledge(struct edp_client *client, unsigned int required, unsigned int net, 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 * e0_level(c) + net - 1) / net; c->gwt = edp_throttling_point(c, step ?: 1); *pledged += cur_level(c) - c->states[c->gwt]; if (*pledged >= required) break; } 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; net = net_e0(client); if (!net) { WARN_ON(1); return; } ar = approvable_req(client, net); 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, &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 fair_update_request(struct edp_client *client, const unsigned int *req) { edp_default_update_request(client, req, throttle); } static unsigned int fair_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 unsigned int promotion_pledge(struct edp_manager *m, unsigned int net) { unsigned int budget = m->remaining; unsigned int unpledged = m->remaining; unsigned int denied = m->num_denied; struct edp_client *c; unsigned int step; list_for_each_entry(c, &m->clients, link) { if (req_level(c) <= cur_level(c) || !c->notify_promotion) continue; step = (e0_level(c) * budget + net - 1) / net; step = min(step, unpledged); c->gwt = fair_promotion_point(c, step, unpledged); unpledged -= c->states[c->gwt] - cur_level(c); if (req_index(c) == c->gwt) denied--; if (!unpledged || !denied) break; } return unpledged; } static void fair_promote(struct edp_manager *mgr) { unsigned int net = 0; struct edp_client *c; unsigned int step; unsigned int pp; unsigned int unpledged; list_for_each_entry(c, &mgr->clients, link) { if (req_level(c) > cur_level(c) && c->notify_promotion) { net += e0_level(c); c->gwt = cur_index(c); } } /* if the net is 0, fall back on priority */ unpledged = net ? promotion_pledge(mgr, net) : mgr->remaining; list_for_each_entry(c, &mgr->clients, link) { if (req_level(c) <= cur_level(c) || !c->notify_promotion || c->gwt == cur_index(c)) continue; pp = c->gwt; /* make sure that the unpledged current is not wasted */ if (unpledged && req_index(c) != pp) { step = c->states[pp] - cur_level(c) + unpledged; pp = edp_promotion_point(c, step); unpledged -= c->states[pp] - c->states[c->gwt]; } 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 fair_governor = { .name = "fair", .owner = THIS_MODULE, .update_request = fair_update_request, .update_loans = edp_default_update_loans, .promote = fair_promote }; static int __init fair_init(void) { return edp_register_governor(&fair_governor); } postcore_initcall(fair_init);