pm: EDP: Add governor framework
Sivaram Nair [Wed, 22 Aug 2012 07:13:48 +0000 (10:13 +0300)]
This patch introduces the governor framework into EDP. Governor will
handle all request related processing including issuing of notifications
and throttling.

A single governor can be used by multiple managers.

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

Rebase-Id: Rfd6dca218da5cdeb0fe36f55d9e8facee57e10ac

drivers/edp/edp.c
drivers/edp/edp_internal.h [new file with mode: 0644]
include/linux/edp.h

index 4166f83..0081975 100644 (file)
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/edp.h>
-
-struct loan_client {
-       struct list_head link;
-       struct edp_client *client;
-       unsigned int size;
-};
+#include "edp_internal.h"
 
 DEFINE_MUTEX(edp_lock);
 static LIST_HEAD(edp_managers);
+static LIST_HEAD(edp_governors);
 
 static struct edp_manager *find_manager(const char *name)
 {
@@ -45,6 +41,14 @@ static struct edp_manager *find_manager(const char *name)
        return NULL;
 }
 
+static void promote(struct work_struct *work)
+{
+       struct edp_manager *m = container_of(work, struct edp_manager, work);
+       mutex_lock(&edp_lock);
+       m->gov->promote(m);
+       mutex_unlock(&edp_lock);
+}
+
 int edp_register_manager(struct edp_manager *mgr)
 {
        int r = -EEXIST;
@@ -57,7 +61,9 @@ int edp_register_manager(struct edp_manager *mgr)
                list_add_tail(&mgr->link, &edp_managers);
                mgr->registered = true;
                mgr->remaining = mgr->imax;
+               mgr->gov = NULL;
                INIT_LIST_HEAD(&mgr->clients);
+               INIT_WORK(&mgr->work, promote);
                r = 0;
        }
        mutex_unlock(&edp_lock);
@@ -66,6 +72,42 @@ int edp_register_manager(struct edp_manager *mgr)
 }
 EXPORT_SYMBOL(edp_register_manager);
 
+static int set_governor(struct edp_manager *mgr, struct edp_governor *gov)
+{
+       int r = 0;
+
+       if (mgr ? !mgr->registered : 0)
+               return -EINVAL;
+
+       if (mgr->gov) {
+               cancel_work_sync(&mgr->work);
+               if (mgr->gov->stop)
+                       mgr->gov->stop(mgr);
+               mgr->gov->refcnt--;
+               module_put(mgr->gov->owner);
+               mgr->gov = NULL;
+       }
+
+       if (gov) {
+               if (!gov->refcnt)
+                       return -EINVAL;
+               if (!try_module_get(gov->owner))
+                       return -EINVAL;
+               if (gov->start)
+                       r = gov->start(mgr);
+               if (r) {
+                       module_put(gov->owner);
+                       WARN_ON(1);
+                       return r;
+               }
+
+               gov->refcnt++;
+               mgr->gov = gov;
+       }
+
+       return 0;
+}
+
 int edp_unregister_manager(struct edp_manager *mgr)
 {
        int r = 0;
@@ -79,6 +121,7 @@ int edp_unregister_manager(struct edp_manager *mgr)
        } else if (!list_empty(&mgr->clients)) {
                r = -EBUSY;
        } else {
+               set_governor(mgr, NULL);
                list_del(&mgr->link);
                mgr->registered = false;
        }
@@ -202,49 +245,29 @@ EXPORT_SYMBOL(edp_register_client);
 
 static void update_loans(struct edp_client *client)
 {
-       struct loan_client *p;
-       unsigned int size;
-
-       if (!client->cur || list_empty(&client->borrowers))
-               return;
-
-       size = *client->cur < client->ithreshold ? 0 :
-               *client->cur - client->ithreshold;
-
-       /* TODO: multi-party loans */
-       if (!list_is_singular(&client->borrowers)) {
-               WARN_ON(1);
-               return;
-       }
-
-       p = list_first_entry(&client->borrowers, struct loan_client, link);
-
-       /* avoid spurious change notifications */
-       if (size != p->size) {
-               p->size = size;
-               p->client->notify_loan_update(size, client);
+       struct edp_governor *gov;
+       gov = client->manager ? client->manager->gov : NULL;
+       if (gov && client->cur && !list_empty(&client->borrowers)) {
+               if (gov->update_loans)
+                       gov->update_loans(client);
        }
 }
 
 static int mod_request(struct edp_client *client, const unsigned int *req)
 {
-       unsigned int old = client->cur ? *client->cur : 0;
-       unsigned int new = req ? *req : 0;
-       unsigned int need;
+       struct edp_manager *m = client->manager;
+       unsigned int prev_remain = m->remaining;
 
-       if (new < old) {
-               client->cur = req;
-               client->manager->remaining += old - new;
-       } else {
-               need = new - old;
-               if (need > client->manager->remaining)
-                       return -ENODEV;
-               client->manager->remaining -= need;
-               client->cur = req;
-       }
+       if (!m->gov)
+               return -ENODEV;
 
+       m->gov->update_request(client, req);
        update_loans(client);
 
+       /* Do not block calling clients for promotions */
+       if (m->remaining > prev_remain && m->num_denied && m->gov->promote)
+               schedule_work(&m->work);
+
        return 0;
 }
 
@@ -476,3 +499,80 @@ int edp_update_loan_threshold(struct edp_client *client, unsigned int threshold)
        return r;
 }
 EXPORT_SYMBOL(edp_update_loan_threshold);
+
+static struct edp_governor *find_governor(const char *s)
+{
+       struct edp_governor *g;
+
+       list_for_each_entry(g, &edp_governors, link)
+               if (!strnicmp(s, g->name, EDP_NAME_LEN))
+                       return g;
+
+       return NULL;
+}
+
+int edp_register_governor(struct edp_governor *gov)
+{
+       int r = 0;
+
+       if (!gov)
+               return -EINVAL;
+
+       if (!gov->update_request)
+               return -EINVAL;
+
+       mutex_lock(&edp_lock);
+       if (find_governor(gov->name)) {
+               r = -EEXIST;
+       } else {
+               gov->refcnt = 1;
+               list_add(&gov->link, &edp_governors);
+       }
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_register_governor);
+
+int edp_unregister_governor(struct edp_governor *gov)
+{
+       int r = 0;
+
+       mutex_lock(&edp_lock);
+       if (!gov) {
+               r = -EINVAL;
+       } else if (gov->refcnt != 1) {
+               r = gov->refcnt > 1 ? -EBUSY : -ENODEV;
+       } else {
+               list_del(&gov->link);
+               gov->refcnt = 0;
+       }
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_unregister_governor);
+
+struct edp_governor *edp_get_governor(const char *name)
+{
+       struct edp_governor *g;
+
+       mutex_lock(&edp_lock);
+       g = find_governor(name);
+       mutex_unlock(&edp_lock);
+
+       return g;
+}
+EXPORT_SYMBOL(edp_get_governor);
+
+int edp_set_governor(struct edp_manager *mgr, struct edp_governor *gov)
+{
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = set_governor(mgr, gov);
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_set_governor);
diff --git a/drivers/edp/edp_internal.h b/drivers/edp/edp_internal.h
new file mode 100644 (file)
index 0000000..636a857
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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/>.
+ */
+
+#ifndef _EDP_INTERNAL_H
+#define _EDP_INTERNAL_H
+
+#include <linux/kernel.h>
+#include <linux/edp.h>
+
+struct loan_client {
+       struct list_head link;
+       struct edp_client *client;
+       unsigned int size;
+};
+
+static inline unsigned int cur_level(struct edp_client *c)
+{
+       return c->cur ? *c->cur : 0;
+}
+
+static inline unsigned int req_level(struct edp_client *c)
+{
+       return c->req ? *c->req : 0;
+}
+
+static inline unsigned int e0_level(struct edp_client *c)
+{
+       return c->states[c->e0_index];
+}
+
+static inline unsigned int cur_index(struct edp_client *c)
+{
+       return c->cur ? c->cur - c->states : c->num_states;
+}
+
+static inline unsigned int req_index(struct edp_client *c)
+{
+       return c->req ? c->req - c->states : c->num_states;
+}
+
+#endif
index eeb09e4..9cc59f3 100644 (file)
@@ -33,6 +33,9 @@ struct edp_manager {
        struct list_head clients;
        bool registered;
        unsigned int remaining;
+       struct edp_governor *gov;
+       struct work_struct work;
+       unsigned int num_denied;
 };
 
 /*
@@ -42,7 +45,9 @@ struct edp_manager {
  * @e0_index: index of the E0 state in the above array
  * @max_borrowers: maximum number of clients allowed to borrow from this
  * @priority: client priority - should be between EDP_MIN_PRIO & EDP_MAX_PRIO
+ * @throttle: throttle callback function
  * @notify_loan_update: for receiving loan size change notifications
+ *     (clients should return the amount of loan consumed)
  * @notify_loan_close: for receiving loan closure notification
  * Note that each EDP client is tied to a single EDP manager
  */
@@ -54,7 +59,9 @@ struct edp_client {
        unsigned int max_borrowers;
        int priority;
 
-       void (*notify_loan_update)(unsigned int new_size,
+       void (*throttle)(unsigned int new_state);
+       void (*notify_promotion)(unsigned int new_state);
+       unsigned int (*notify_loan_update)(unsigned int new_size,
                        struct edp_client *lender);
        void (*notify_loan_close)(struct edp_client *lender);
 
@@ -67,6 +74,25 @@ struct edp_client {
        unsigned int ithreshold;
        unsigned int num_borrowers;
        unsigned int num_loans;
+
+       /* governor internal */
+       unsigned int gwt;
+};
+
+struct edp_governor {
+       char name[EDP_NAME_LEN];
+       struct module *owner;
+
+       int (*start)(struct edp_manager *mgr);
+       void (*stop)(struct edp_manager *mgr);
+       void (*update_request)(struct edp_client *client,
+                       const unsigned int *req);
+       void (*update_loans)(struct edp_client *client);
+       void (*promote)(struct edp_manager *mgr);
+
+       /* internal */
+       struct list_head link;
+       unsigned int refcnt;
 };
 
 #ifdef CONFIG_EDP_FRAMEWORK
@@ -109,6 +135,11 @@ extern int edp_unregister_loan(struct edp_client *lender,
                struct edp_client *borrower);
 extern int edp_update_loan_threshold(struct edp_client *lender,
                unsigned int threshold);
+
+extern int edp_register_governor(struct edp_governor *gov);
+extern int edp_unregister_governor(struct edp_governor *gov);
+extern struct edp_governor *edp_get_governor(const char *name);
+extern int edp_set_governor(struct edp_manager *mgr, struct edp_governor *gov);
 #else
 static inline int edp_register_manager(struct edp_manager *mgr)
 { return -ENODEV; }
@@ -135,6 +166,15 @@ static inline int edp_unregister_loan(struct edp_client *lender,
 static inline int edp_update_loan_threshold(struct edp_client *lender,
                unsigned int threshold)
 { return -ENODEV; }
+static inline int edp_register_governor(struct edp_governor *gov)
+{ return -ENODEV; }
+static inline int edp_unregister_governor(struct edp_governor *gov)
+{ return -ENODEV; }
+static inline struct edp_governor *edp_get_governor(const char *name)
+{ return NULL; }
+static inline int edp_set_governor(struct edp_manager *mgr,
+               struct edp_governor *gov)
+{ return -ENODEV; }
 #endif
 
 #endif