pm: EDP: loan handling
Sivaram Nair [Thu, 12 Jul 2012 07:06:58 +0000 (10:06 +0300)]
Added EDP loan APIs and single-borrower implementation.

Change-Id: Ib4c3777d7173d16d5d002fd28e8f74f1b6add239
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/115713
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com>
Reviewed-by: Peter De Schrijver <pdeschrijver@nvidia.com>

drivers/edp/edp.c
include/linux/edp.h

index 63a7df1..900c228 100644 (file)
 #include <linux/string.h>
 #include <linux/mutex.h>
 #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;
+};
+
 DEFINE_MUTEX(edp_lock);
 static LIST_HEAD(edp_managers);
 
@@ -159,6 +166,10 @@ static int register_client(struct edp_manager *mgr, struct edp_client *client)
        client->manager = mgr;
        client->req = NULL;
        client->cur = NULL;
+       INIT_LIST_HEAD(&client->borrowers);
+       client->num_borrowers = 0;
+       client->num_loans = 0;
+       client->ithreshold = client->states[0];
 
        return 0;
 }
@@ -175,6 +186,32 @@ int edp_register_client(struct edp_manager *mgr, struct edp_client *client)
 }
 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);
+       }
+}
+
 static int mod_request(struct edp_client *client, const unsigned int *req)
 {
        unsigned int old = client->cur ? *client->cur : 0;
@@ -192,17 +229,45 @@ static int mod_request(struct edp_client *client, const unsigned int *req)
                client->cur = req;
        }
 
+       update_loans(client);
+
        return 0;
 }
 
+static void del_borrower(struct edp_client *lender, struct loan_client *pcl)
+{
+       pcl->client->notify_loan_close(lender);
+       lender->num_borrowers--;
+       pcl->client->num_loans--;
+       list_del(&pcl->link);
+       kfree(pcl);
+}
+
+static void close_all_loans(struct edp_client *client)
+{
+       struct loan_client *p;
+
+       while (!list_empty(&client->borrowers)) {
+               p = list_first_entry(&client->borrowers, struct loan_client,
+                               link);
+               del_borrower(client, p);
+       }
+}
+
+static inline bool registered_client(struct edp_client *client)
+{
+       return client ? client->manager : false;
+}
+
 static int unregister_client(struct edp_client *client)
 {
-       if (!client)
+       if (!registered_client(client))
                return -EINVAL;
 
-       if (!client->manager)
-               return -ENODEV;
+       if (client->num_loans)
+               return -EBUSY;
 
+       close_all_loans(client);
        mod_request(client, NULL);
        list_del(&client->link);
        client->manager = NULL;
@@ -227,12 +292,9 @@ static int update_client_request(struct edp_client *client, unsigned int req,
 {
        int r;
 
-       if (!client)
+       if (!registered_client(client))
                return -EINVAL;
 
-       if (!client->manager)
-               return -ENODEV;
-
        if (req >= client->num_states)
                return -EINVAL;
 
@@ -255,3 +317,133 @@ int edp_update_client_request(struct edp_client *client, unsigned int req,
        return r;
 }
 EXPORT_SYMBOL(edp_update_client_request);
+
+static struct edp_client *get_client(const char *name)
+{
+       struct edp_client *client;
+       struct edp_manager *mgr;
+
+       if (!name)
+               return NULL;
+
+       list_for_each_entry(mgr, &edp_managers, link) {
+               client = find_client(mgr, name);
+               if (client)
+                       return client;
+       }
+
+       return NULL;
+}
+
+struct edp_client *edp_get_client(const char *name)
+{
+       struct edp_client *client;
+
+       mutex_lock(&edp_lock);
+       client = get_client(name);
+       mutex_unlock(&edp_lock);
+
+       return client;
+}
+EXPORT_SYMBOL(edp_get_client);
+
+static struct loan_client *find_borrower(struct edp_client *lender,
+               struct edp_client *borrower)
+{
+       struct loan_client *p;
+
+       list_for_each_entry(p, &lender->borrowers, link)
+               if (p->client == borrower)
+                       return p;
+       return NULL;
+}
+
+static int register_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+       struct loan_client *p;
+
+       if (!registered_client(lender) || !registered_client(borrower))
+               return -EINVAL;
+
+       if (lender->manager != borrower->manager ||
+                       !borrower->notify_loan_update ||
+                       !borrower->notify_loan_close)
+               return -EINVAL;
+
+       if (find_borrower(lender, borrower))
+               return -EEXIST;
+
+       if (lender->num_borrowers >= lender->max_borrowers)
+               return -EBUSY;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       p->client = borrower;
+       lender->num_borrowers++;
+       borrower->num_loans++;
+       list_add_tail(&p->link, &lender->borrowers);
+
+       update_loans(lender);
+       return 0;
+}
+
+int edp_register_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = register_loan(lender, borrower);
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_register_loan);
+
+static int unregister_loan(struct edp_client *lender,
+               struct edp_client *borrower)
+{
+       struct loan_client *p;
+
+       if (!registered_client(lender) || !registered_client(borrower))
+               return -EINVAL;
+
+       p = find_borrower(lender, borrower);
+       if (!p)
+               return -EINVAL;
+
+       del_borrower(lender, p);
+       update_loans(lender);
+       return 0;
+}
+
+int edp_unregister_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = unregister_loan(lender, borrower);
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_unregister_loan);
+
+int edp_update_loan_threshold(struct edp_client *client, unsigned int threshold)
+{
+       int r = -EINVAL;
+
+       mutex_lock(&edp_lock);
+
+       if (registered_client(client)) {
+               client->ithreshold = threshold;
+               update_loans(client);
+               r = 0;
+       }
+
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_update_loan_threshold);
index 743d39a..571c3ef 100644 (file)
@@ -40,6 +40,9 @@ struct edp_manager {
  *     This must be sorted in descending order.
  * @num_states: length of the above array
  * @e0_index: index of the E0 state in the above array
+ * @max_borrowers: maximum number of clients allowed to borrow from this
+ * @notify_loan_update: for receiving loan size change notifications
+ * @notify_loan_close: for receiving loan closure notification
  * Note that each EDP client is tied to a single EDP manager
  */
 struct edp_client {
@@ -47,12 +50,21 @@ struct edp_client {
        const unsigned int *const states;
        const unsigned int num_states;
        const unsigned int e0_index;
+       const unsigned int max_borrowers;
+
+       void (*notify_loan_update)(unsigned int new_size,
+                       struct edp_client *lender);
+       void (*notify_loan_close)(struct edp_client *lender);
 
        /* internal */
        struct list_head link;
+       struct list_head borrowers;
        struct edp_manager *manager;
        const unsigned int *req;
        const unsigned int *cur;
+       unsigned int ithreshold;
+       unsigned int num_borrowers;
+       unsigned int num_loans;
 };
 
 #ifdef CONFIG_EDP_FRAMEWORK
@@ -65,6 +77,36 @@ extern int edp_register_client(struct edp_manager *mgr,
 extern int edp_unregister_client(struct edp_client *client);
 extern int edp_update_client_request(struct edp_client *client,
                unsigned int req, unsigned int *approved);
+extern struct edp_client *edp_get_client(const char *name);
+
+/*
+ * EDP lender: An EDP client whose device (a) typically draws current less
+ * than some (dynamically varying) threshold (b) occasionally draws more
+ * than its threshold but not more than what is allowed by its current E-state
+ * and (c) asserts a side-band signal prior to exceeding the threshold
+ *
+ * EDP borrower: An EDP client which (a) gets its base current consumption
+ * budget by setting an E-state with the EDP manager (b) borrows from a
+ * lender's additional current budget according to the difference between
+ * its E-state and threshold when the side-band is deasserted and
+ * (c) stops borrowing whenever the side-band is asserted
+ *
+ * EDP loan: a contract allowing an EDP borrower to borrow from a lender.
+ *
+ * The loan is registered via edp_register_loan. In order to register
+ * a loan, both the lender and borrower needs to be registered with the same
+ * manager. The contract is terminated with edp_unregister_loan.
+ * The lender updates its threshold values using edp_update_loan_threshold.
+ * Whenever there is a change in the loan size (due to a change in the
+ * lender's E-state or threshold), the borrowr is notified through
+ * notify_loan_update.
+ */
+extern int edp_register_loan(struct edp_client *lender,
+               struct edp_client *borrower);
+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);
 #else
 static inline int edp_register_manager(struct edp_manager *mgr)
 { return -ENODEV; }
@@ -80,6 +122,17 @@ static inline int edp_unregister_client(struct edp_client *client)
 static inline int edp_update_client_request(struct edp_client *client,
                unsigned int req, unsigned int *approved)
 { return -ENODEV; }
+static inline struct edp_client *edp_get_client(const char *name)
+{ return NULL; }
+static inline int edp_register_loan(struct edp_client *lender,
+               struct edp_client *borrower)
+{ return -ENODEV; }
+static inline int edp_unregister_loan(struct edp_client *lender,
+               struct edp_client *borrower)
+{ return -ENODEV; }
+static inline int edp_update_loan_threshold(struct edp_client *lender,
+               unsigned int threshold)
+{ return -ENODEV; }
 #endif
 
 #endif