pm: EDP: adding priority governor
Sivaram Nair [Wed, 22 Aug 2012 07:49:09 +0000 (10:49 +0300)]
This patch adds the priority governor to EDP framework. When throttling
is required, lower priority clients are picked first.

Change-Id: I9e673f35dbe98728bb2fbd9e4ca1c7af684e1d1c
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/125226
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

drivers/edp/Makefile
drivers/edp/edp_priority.c [new file with mode: 0644]

index c39ed9a..134381f 100644 (file)
@@ -1 +1,2 @@
 obj-$(CONFIG_EDP_FRAMEWORK)    += edp.o
+obj-$(CONFIG_EDP_FRAMEWORK)    += edp_priority.o
diff --git a/drivers/edp/edp_priority.c b/drivers/edp/edp_priority.c
new file mode 100644 (file)
index 0000000..c796fb0
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2012, 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"
+
+/*
+ * Calculate the approvable E-state for the requesting client.
+ * Non-negative E-state requests are always approved.  A (higher)
+ * negative E-state request is approved only if lower priority clients
+ * can be throttled (at most to E0) in order to recover the necessary
+ * power deficit. If this can not be met, a lower E-state is approved
+ * (at least E0).
+ */
+static unsigned int approvable_req(struct edp_client *client)
+{
+       struct edp_manager *m = client->manager;
+       unsigned int old = cur_level(client);
+       unsigned int deficit = *client->req - old;
+       unsigned int recoverable = m->remaining;
+       unsigned int i = req_index(client);
+       struct edp_client *p = client;
+
+       if (i >= client->e0_index)
+               return i;
+
+       list_for_each_entry_continue(p, &m->clients, link) {
+               if (cur_level(p) > e0_level(p)) {
+                       recoverable += cur_level(p) - e0_level(p);
+                       if (recoverable >= deficit)
+                               return i;
+               }
+       }
+
+       while (i < client->e0_index && recoverable < deficit) {
+               i++;
+               deficit = client->states[i] - old;
+       }
+
+       return i;
+}
+
+static unsigned int throttling_point(struct edp_client *c,
+               unsigned int deficit)
+{
+       unsigned int lim;
+       unsigned int i;
+
+       if (cur_level(c) - e0_level(c) <= deficit)
+               return c->e0_index;
+
+       lim = cur_level(c) - deficit;
+       i = cur_index(c);
+       while (i < c->e0_index && c->states[i] > lim)
+               i++;
+
+       return i;
+}
+
+static void throttle(struct edp_client *client)
+{
+       unsigned int ar;
+       unsigned int deficit;
+       struct edp_client *p;
+       struct edp_manager *m = client->manager;
+       unsigned int pledged = m->remaining;
+       unsigned int recovered = m->remaining;
+
+       /* Check if we can satisfy the request as it is */
+       ar = approvable_req(client);
+       deficit = client->states[ar] - cur_level(client);
+       if (m->remaining >= deficit)
+               goto ret;
+
+       /*
+        * We do the throttling in two steps: first we will identify and
+        * mark the clients starting from the lower priority ones. We
+        * stop when we find the highest priority client that should be
+        * throttled.
+        */
+       list_for_each_entry_reverse(p, &m->clients, link) {
+               if (p == client || cur_level(p) <= e0_level(p))
+                       continue;
+
+               p->gwt = throttling_point(p, deficit - pledged);
+               pledged += cur_level(p) - p->states[p->gwt];
+               if (pledged >= deficit)
+                       break;
+       }
+
+       /*
+        * By now, we are guaranteed to have at least the adjusted
+        * deficit - may be even more.
+        */
+       WARN_ON(pledged < deficit);
+
+       /*
+        * We now do the actual throttling starting from where we stoped
+        * in step 1 and going in the opposite direction. This way we
+        * can avoid situations where clients are throttled needlessly
+        * and promoted back immediately.
+        */
+       list_for_each_entry_from(p, &m->clients, link) {
+               if (p == client || cur_level(p) <= e0_level(p))
+                       continue;
+
+               WARN_ON(!p->throttle);
+               p->throttle(p->gwt);
+               recovered += cur_level(p) - p->states[p->gwt];
+               if (p->cur == p->req)
+                       m->num_denied++;
+
+               p->cur = p->states + p->gwt;
+               if (recovered >= deficit)
+                       break;
+       }
+
+ret:
+       client->cur = client->states + ar;
+       m->remaining = recovered - deficit;
+}
+
+static void prio_update_request(struct edp_client *client,
+               const unsigned int *req)
+{
+       struct edp_manager *m = client->manager;
+       unsigned int old = cur_level(client);
+       unsigned int new = req ? *req : 0;
+       bool was_denied = client->cur != client->req;
+
+       client->req = req;
+
+       if (new < old) {
+               client->cur = req;
+               m->remaining += old - new;
+       } else if (new - old <= m->remaining) {
+               client->cur = req;
+               m->remaining -= new - old;
+       } else {
+               throttle(client);
+       }
+
+       if (was_denied && client->cur == client->req)
+               m->num_denied--;
+       else if (!was_denied && client->cur != client->req)
+               m->num_denied++;
+}
+
+static void prio_update_loans(struct edp_client *lender)
+{
+       unsigned int size = *lender->cur <= lender->ithreshold ? 0 :
+               *lender->cur - lender->ithreshold;
+       struct loan_client *p;
+
+       list_for_each_entry(p, &lender->borrowers, link) {
+               if (!size)
+                       return;
+
+               if (size != p->size) {
+                       p->size = p->client->notify_loan_update(size, lender);
+                       WARN_ON(p->size > size);
+                       size -= min(p->size, size);
+               }
+       }
+}
+
+static void prio_promote(struct edp_manager *mgr)
+{
+       struct edp_client *p;
+       unsigned int delta;
+
+       list_for_each_entry(p, &mgr->clients, link) {
+               if (!mgr->num_denied || !mgr->remaining)
+                       return;
+
+               delta = req_level(p) - cur_level(p);
+               if (delta && delta <= mgr->remaining && p->notify_promotion) {
+                       p->cur = p->req;
+                       mgr->num_denied--;
+                       mgr->remaining -= delta;
+                       p->notify_promotion(cur_index(p));
+               }
+       }
+}
+
+static struct edp_governor prio_governor = {
+       .name = "priority",
+       .owner = THIS_MODULE,
+       .update_request = prio_update_request,
+       .update_loans = prio_update_loans,
+       .promote = prio_promote
+};
+
+static int __init prio_init(void)
+{
+       return edp_register_governor(&prio_governor);
+}
+
+static void __exit prio_exit(void)
+{
+       int r = edp_unregister_governor(&prio_governor);
+       WARN_ON(r);
+}
+
+MODULE_LICENSE("GPL");
+module_init(prio_init);
+module_exit(prio_exit);