PM / QoS: add pm_qos_update_request_timeout() API
MyungJoo Ham [Wed, 28 Mar 2012 21:31:24 +0000 (23:31 +0200)]
The new API, pm_qos_update_request_timeout() is to provide a timeout
with pm_qos_update_request.

For example, pm_qos_update_request_timeout(req, 100, 1000), means that
QoS request on req with value 100 will be active for 1000 microseconds.
After 1000 microseconds, the QoS request thru req is reset. If there
were another pm_qos_update_request(req, x) during the 1000 us, this
new request with value x will override as this is another request on the
same req handle. A new request on the same req handle will always
override the previous request whether it is the conventional request or
it is the new timeout request.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Mark Gross <markgross@thegnar.org>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

include/linux/pm_qos.h
kernel/power/qos.c

index afc8cec..dd93655 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/notifier.h>
 #include <linux/miscdevice.h>
 #include <linux/device.h>
+#include <linux/workqueue.h>
 
 enum {
        PM_QOS_RESERVED = 0,
@@ -38,6 +39,7 @@ enum {
 struct pm_qos_request {
        struct plist_node node;
        int pm_qos_class;
+       struct delayed_work work; /* for pm_qos_update_request_timeout */
 };
 
 struct dev_pm_qos_request {
@@ -82,6 +84,8 @@ void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
                        s32 value);
 void pm_qos_update_request(struct pm_qos_request *req,
                           s32 new_value);
+void pm_qos_update_request_timeout(struct pm_qos_request *req,
+                                  s32 new_value, unsigned long timeout_us);
 void pm_qos_remove_request(struct pm_qos_request *req);
 
 int pm_qos_request(int pm_qos_class);
index be37626..834fbfc 100644 (file)
@@ -291,6 +291,21 @@ int pm_qos_request_active(struct pm_qos_request *req)
 EXPORT_SYMBOL_GPL(pm_qos_request_active);
 
 /**
+ * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout
+ * @work: work struct for the delayed work (timeout)
+ *
+ * This cancels the timeout request by falling back to the default at timeout.
+ */
+static void pm_qos_work_fn(struct work_struct *work)
+{
+       struct pm_qos_request *req = container_of(to_delayed_work(work),
+                                                 struct pm_qos_request,
+                                                 work);
+
+       pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE);
+}
+
+/**
  * pm_qos_add_request - inserts new qos request into the list
  * @req: pointer to a preallocated handle
  * @pm_qos_class: identifies which list of qos request to use
@@ -314,6 +329,7 @@ void pm_qos_add_request(struct pm_qos_request *req,
                return;
        }
        req->pm_qos_class = pm_qos_class;
+       INIT_DELAYED_WORK(&req->work, pm_qos_work_fn);
        pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints,
                             &req->node, PM_QOS_ADD_REQ, value);
 }
@@ -340,6 +356,9 @@ void pm_qos_update_request(struct pm_qos_request *req,
                return;
        }
 
+       if (delayed_work_pending(&req->work))
+               cancel_delayed_work_sync(&req->work);
+
        if (new_value != req->node.prio)
                pm_qos_update_target(
                        pm_qos_array[req->pm_qos_class]->constraints,
@@ -348,6 +367,34 @@ void pm_qos_update_request(struct pm_qos_request *req,
 EXPORT_SYMBOL_GPL(pm_qos_update_request);
 
 /**
+ * pm_qos_update_request_timeout - modifies an existing qos request temporarily.
+ * @req : handle to list element holding a pm_qos request to use
+ * @new_value: defines the temporal qos request
+ * @timeout_us: the effective duration of this qos request in usecs.
+ *
+ * After timeout_us, this qos request is cancelled automatically.
+ */
+void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value,
+                                  unsigned long timeout_us)
+{
+       if (!req)
+               return;
+       if (WARN(!pm_qos_request_active(req),
+                "%s called for unknown object.", __func__))
+               return;
+
+       if (delayed_work_pending(&req->work))
+               cancel_delayed_work_sync(&req->work);
+
+       if (new_value != req->node.prio)
+               pm_qos_update_target(
+                       pm_qos_array[req->pm_qos_class]->constraints,
+                       &req->node, PM_QOS_UPDATE_REQ, new_value);
+
+       schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us));
+}
+
+/**
  * pm_qos_remove_request - modifies an existing qos request
  * @req: handle to request list element
  *
@@ -366,6 +413,9 @@ void pm_qos_remove_request(struct pm_qos_request *req)
                return;
        }
 
+       if (delayed_work_pending(&req->work))
+               cancel_delayed_work_sync(&req->work);
+
        pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints,
                             &req->node, PM_QOS_REMOVE_REQ,
                             PM_QOS_DEFAULT_VALUE);