pass: rescon: Add support for multiple scenario pass_level 17/224717/7
authorChanwoo Choi <cw00.choi@samsung.com>
Tue, 28 Jan 2020 05:47:44 +0000 (14:47 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Wed, 26 Feb 2020 10:22:17 +0000 (19:22 +0900)
PASS provides multiple modules like CPUHP (CPU Hotplug Manager), PMQoS,
Thermal Monitor and so on. Each module requires their own pass_level
in order to change h/w resources such as the number of online CPU,
the minimum/maximum frequency of GPU.

Prior to that, PASS supports only 'pass_level'. If user of PASS want to
set 'AppLaunch' scenario of PMQoS module, PMQoS only requires the range
of 'pass_level' like the minimum and maximum 'pass_level'. It is not enough
to meet the requirements from 'AppLaunch' scenario. Some scenario might
require the more detailed changes than already defined 'pass_level'.

In result of these requirements, add support for new 'scenario pass_level'
for scenarios such as AppLaunch of PMQoS and WarningAction of Thermal
Monitor. 'scenario pass_level' is possible to specify the detailed value
of h/w resources such as the minimum/maximum frequency (limit_(min|max)_freq),
the minimum/maximum number of online CPU (limit_(min|max)_cpu)
and fault_around_bytes for memorh h/w resource.

In order to support the multiple scenario pass_level at the same time,
RESCON (Resource Controller) module adds new following functions and then
delete previous function.

PMQoS will use new functions for supporting the 'scenario pass_level'
and Thermal Monitor will use the new functions for 'scenario pass_level'
for constrainting the h/w resouece to prevent the dangerous overheating.

[Added new functions]
- int pass_rescon_sync(struct pass_resource *res);
- int pass_rescon_set_level_sync(struct pass_resource *res, int level);
- int pass_rescon_set_scenario_level_sync(struct pass_resource *res,
int scenario_level);
- int pass_rescon_unset_scenario_level_sync(struct pass_resource *res,
int scenario_level);
- int pass_rescon_set_scenario_level(struct pass_resource *res,
int scenario_level);
- int pass_rescon_unset_scenario_level(struct pass_resource *res,
int scenario_level);

[Deleted functions]
- int pass_rescon_set_level_scope(struct pass_resource *res, int new_level,
int min_level, int max_level, void *data);

[Deprecated functions. Because these functions are keeping
for the compatibility with legacy feature. It should be used on PMQoS module.]
- int pass_rescon_set_scenario_level_sync_with_data(struct pass_resource *res,
int scenario_level, void *data);
- int pass_rescon_unset_scenario_level_sync_with_data(struct pass_resource *res,
int scenario_level, void *data);

[Exmaple of multiple scenario pass_level on PMQoS and Thermal Monitor modules]
Case 1. AppLaunch scenario of PMQoS ,
- 'AppLaunch' scenario contains 'scenario_level=1' property as following:
[ScenarioLevel1]
limit_min_freq=1300000
limit_max_freq=1300000
limit_min_cpu=3
limit_max_cpu=4

- In result, RESCON (Resource Controller) set h/w resource as following:
The minimum frequency is 1300MHz.
The maximum frequency is 1300MHz.
The minimum number of online CPU is 3.
The maximum number of online CPU is 4.
-> It maintains 1300MHz and three online CPU.

Case 2. Warning scenario of Thermal Monitor,
- 'Warning' scenario contains 'scenario_level=2' property as following:
[ScenarioLevel2]
limit_min_freq=768000
limit_max_freq=1000000
limit_min_cpu=0
limit_max_cpu=2

- In result, RESCON (Resource Controller) set h/w resource as following:
The minimum frequency is 768MHz.
The maximum frequency is 1000MHz.
The minimum number of online CPU has no any constraint.
The maximum number of online CPU is 2.
-> It maintain 768MHz and doesn't turn on more than 2 CPU.

Case 3. AppLaunch scenario of PMQoS + Warning scenario of Thermal Monitor,
- 'AppLaunch' scenario contains 'scenario_level=1' property and
  'Warning' scenario contains 'scenario_level=2' property as following:

[ScenarioLevel1]
limit_min_freq=1300000
limit_max_freq=1300000
limit_min_cpu=3
limit_max_cpu=4

[ScenarioLevel2]
limit_min_freq=768000
limit_max_freq=1000000
limit_min_cpu=0
limit_max_cpu=2

- In result, RESCON (Resource Controller) set h/w resource as following:
The minimum frequency is 1000MHz because MAX(1300000, 768000) cannot be
                                 over thatn maximum frequency (1000MHz).
The maximum frequency is 1000MHz.
The minimum number of online CPU is 2.
The maximum number of online CPU is 2.
-> It maintain 1000MHz and doesn't turn on more than 2 CPU.

[ScenarioLevel0]
limit_min_freq=768000
limit_max_freq=1300000
limit_min_cpu=0
limit_max_cpu=4

[ScenarioLevel1]
limit_min_freq=1300000
limit_max_freq=1300000
limit_min_cpu=3
limit_max_cpu=4

[ScenarioLevel2]
limit_min_freq=768000
limit_max_freq=1000000
limit_min_cpu=0
limit_max_cpu=2

[PassScenario]
pass_scenario_support=yes
pass_num_scenarios=1

[Scenario0]
name=AppLaunch
support=yes
scenario_level=1 <- It indicates 'ScenarioLevel1' for AppLaunch scenario.

[thermal]
thermal_support=yes
thermal_number_of_scenario=2
thermal_timer_interval_ms=5000

[thermal.scenario0]
support=yes
name=Release
temperature=25
timer_interval_ms=5000
scenario_level=0

[thermal.scenario1]
support=yes
name=Warning
temperature=30
timer_interval_ms=3000
scenario_level=2 <- It indicates 'ScenarioLevel2' for Warning scenario.

Change-Id: I45e99b1794b85342f29bf0decec6a859f56f8f58
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
src/pass/pass-cpuhp.c
src/pass/pass-parser.c
src/pass/pass-pmqos.c
src/pass/pass-rescon.c
src/pass/pass-rescon.h
src/pass/pass.h

index d659416e013c3bf14719fe8c7d3d05982e0dd9c0..5345e03ede31a189807e53f8f5a0b5dc4c4b700f 100644 (file)
@@ -192,7 +192,7 @@ static int cpuhp_timer_func(void *result, void *user_data)
        if (cpuhp->governor->governor) {
                level = cpuhp->governor->governor(res, result);
 
-               pass_rescon_set_level(res, level);
+               pass_rescon_set_level_sync(res, level);
        } else {
                _E("cannot call the governor function");
                cpuhp_governor_update(res, PASS_OFF);
index ac2d46f04c6329eea9fb604eef553cb3ace7d376..26292a66a3863688483222fda68e995f7cd65593 100644 (file)
@@ -1006,9 +1006,9 @@ int pass_get_each_resource_config(struct pass_resource *res, char *path)
 
        /* Initialize the ResCon's data */
        res->rescon.init_level = INIT_VALUE;
-       res->rescon.prev_level = 0;
-       res->rescon.min_level = 0;
-       res->rescon.max_level = 0;
+       res->rescon.prev_level = INIT_VALUE;
+       res->rescon.min_level = INIT_VALUE;
+       res->rescon.max_level = INIT_VALUE;
        res->rescon.init_scenario_level = INIT_VALUE;
 
        /* Initialize the CPUHP's data */
index dd96e8c2c8000fb27d822c59ebd1e25dabc8fe4b..3fd69a8e90f51805201ab4e5d16d0a26d43809c4 100644 (file)
@@ -74,9 +74,6 @@ static int pmqos_notifier_cb(void *data, void *user_data)
        struct pass_scenario *scn = NULL;
        enum pass_state locked = PASS_UNUSED;
        char name[BUFF_MAX] = {""};
-       unsigned int new_level = 0;
-       unsigned int min_level = 0;
-       unsigned int max_level = 0;
        int index = -1;
 
        if (!res)
@@ -140,52 +137,31 @@ static int pmqos_notifier_cb(void *data, void *user_data)
        scn->pmqos.locked = locked;
 
        if (scn->pmqos.locked) {
-               if (pmqos->num_locked_scenarios == 0)
-                       new_level = pmqos->init_level = res->rescon.curr_level;
-               else
-                       new_level = pmqos->curr_level;
-               min_level = MAX(scn->pmqos.min_level, pmqos->min_level);
-               max_level = MAX(scn->pmqos.max_level, pmqos->max_level);
-
-               if (new_level < min_level)
-                       new_level = min_level;
-               else if (new_level > max_level)
-                       new_level = scn->pmqos.max_level;
-
-               pmqos->curr_level = new_level;
-               pmqos->min_level = min_level;
-               pmqos->max_level = max_level;
-
-               pmqos->num_locked_scenarios++;
-       } else {
-               pmqos->num_locked_scenarios--;
-
-               if (pmqos->num_locked_scenarios == 0) {
-                       new_level = pmqos->init_level;
-                       min_level = res->config_data.default_min_level;
-                       max_level = res->config_data.default_max_level;
+               pass_rescon_set_scenario_level_sync_with_data(res, scn->level, data);
 
-                       pmqos->curr_level = 0;
-                       pmqos->min_level = 0;
-                       pmqos->max_level = 0;
+               if (scn->level < 0) {
+                       _I("Lock   '%s' scenario for '%s' resource\n",
+                               name, config_data->res_name);
                } else {
-                       new_level = pmqos->curr_level;
-                       min_level = pmqos->min_level;
-                       max_level = pmqos->max_level;
+                       _I("Lock   '%s' scenario with 'ScenarioLevel%d' " \
+                               "for '%s' resource\n",
+                               name, scn->level, config_data->res_name);
                }
-       }
-
-       pass_rescon_set_level_scope(res, new_level, min_level, max_level, data);
-
-       if (scn->pmqos.locked) {
-               _I("Lock   '%s' scenario for '%s' resource\n",
-                       name, config_data->res_name);
 
                pmqos->scenarios[index].pmqos.locked_time = get_time_ms();
        } else {
-               _I("UnLock '%s' scenario for '%s' resource (%"PRId64"ms)\n",
-                       name, config_data->res_name,
-                       (get_time_ms() - pmqos->scenarios[index].pmqos.locked_time));
+               if (scn->level < 0) {
+                       _I("UnLock '%s' scenario for '%s' resource (%"PRId64"ms)\n",
+                               name, config_data->res_name,
+                               (get_time_ms() - pmqos->scenarios[index].pmqos.locked_time));
+               } else {
+                       _I("UnLock '%s' scenario with 'ScenarioLevel%d' " \
+                               "for '%s' resource (%"PRId64"ms)\n",
+                               name, scn->level, config_data->res_name,
+                               (get_time_ms() - pmqos->scenarios[index].pmqos.locked_time));
+               }
+
+               pass_rescon_unset_scenario_level_sync_with_data(res, scn->level, data);
 
                pmqos->scenarios[index].pmqos.locked_time = 0;
        }
index c156a17319fd4895d844d53d0c916deedae16d02..0ff6328337ef14dc1a9a682dee6624d0543477d2 100644 (file)
 #include "pass.h"
 #include "pass-hal.h"
 
-/**
- * @brief      Set the next pass_level required from modules like
- *             PASS_MODULE_CPUHP, PASS_MODULE_PMQOS and so on. The demanded
- *             pass_level has the different h/w value such as minimum/maximum
- *             frequency, the number of online CPU, fault_around_bytes and son
- *             on. It changes the h/w value by using the data within the
- *             demanded pass_level and it is final step to change h/w value.
- * @param      [in] res Instance of h/w resource
- * @param      [in] new_level Index of demanded pass_level
- * @return     @c 0 on success, otherwise error value
- */
-static int rescon_set_level(struct pass_resource *res, int new_level)
+#define MAX_INT                                1000000000
+#define MIN_INT                                0
+#define MIN_FAULT_AROUND_BYTES         4096
+#define MAX_FAULT_AROUND_BYTES         65536
+
+static void rescon_print_level(struct pass_resource *res,
+                               struct pass_level *level)
+{
+       if (!res || !level)
+               return;
+
+       /* Add blank line for improving the readability on dlog */
+       _D("");
+
+       if (level->limit_min_freq > 0) {
+               _D("MIN Frequency   is %10d of '%s' resource \n",
+                               level->limit_min_freq,
+                               res->config_data.res_name);
+       }
+
+       if (level->limit_max_freq > 0) {
+               _D("MAX Frequency   is %10d of '%s' resource\n",
+                               level->limit_max_freq,
+                               res->config_data.res_name);
+       }
+
+       if (level->limit_min_cpu >= 0) {
+               _D("MIN CPU number  is %10d of '%s' resource\n",
+                               level->limit_min_cpu,
+                               res->config_data.res_name);
+       }
+
+       if (level->limit_max_cpu >= 0) {
+               _D("MAX CPU number  is %10d of '%s' resource\n",
+                               level->limit_max_cpu,
+                               res->config_data.res_name);
+       }
+
+       if (level->fault_around_bytes > 0) {
+               _D("fault_around_byte is %10d of '%s' resource\n",
+                               level->fault_around_bytes,
+                               res->config_data.res_name);
+       }
+}
+
+static void rescon_adjust_level(struct pass_level *a, struct pass_level *b)
+{
+       /* Adjust limit_min_freq and limit_max_freq */
+       a->limit_min_freq = MAX(a->limit_min_freq, b->limit_min_freq);
+       a->limit_max_freq = MIN(a->limit_max_freq, b->limit_max_freq);
+
+       if (a->limit_min_freq > a->limit_max_freq)
+               a->limit_min_freq = a->limit_max_freq;
+
+       /* Adjust limit_min_cpu and limit_max_cpu */
+       a->limit_min_cpu = MAX(a->limit_min_cpu, b->limit_min_cpu);
+       a->limit_max_cpu = MIN(a->limit_max_cpu, b->limit_max_cpu);
+
+       if (a->limit_min_cpu > a->limit_max_cpu)
+               a->limit_min_cpu = a->limit_max_cpu;
+
+       /* Adjust fault_around_bytes */
+       a->fault_around_bytes = MAX(a->fault_around_bytes,
+                                       b->fault_around_bytes);
+
+       if (a->fault_around_bytes > 0) {
+               if (a->fault_around_bytes < MIN_FAULT_AROUND_BYTES)
+                       a->fault_around_bytes = MIN_FAULT_AROUND_BYTES;
+               if (a->fault_around_bytes > MAX_FAULT_AROUND_BYTES)
+                       a->fault_around_bytes = MAX_FAULT_AROUND_BYTES;
+       }
+}
+
+static int rescon_update(struct pass_resource *res)
 {
+       struct pass_rescon *rescon = &res->rescon;
        struct pass_level *levels = res->config_data.levels;
-       int curr_level = res->rescon.curr_level;
-       int limit_max_freq;
-       int limit_min_freq;
-       int limit_max_cpu;
-       int limit_min_cpu;
-       int fault_around_bytes;
+       struct pass_level *scenario_levels = res->config_data.scenario_levels;
+       struct pass_level adjusted_level;
+       GList *list = rescon->scenario_level_list;
+       int res_type = res->config_data.res_type;
+       int limit_max_freq = -1;
+       int limit_min_freq = -1;
+       int limit_min_cpu = -1;
+       int limit_max_cpu = -1;
+       int fault_around_bytes = -1;
        int ret, i;
+       int failed = 0;
 
-       /* Get the detailed resource value according to PASS level */
-       limit_max_freq = levels[new_level].limit_max_freq;
-       limit_min_freq = levels[new_level].limit_min_freq;
-       limit_max_cpu = levels[new_level].limit_max_cpu;
-       limit_min_cpu = levels[new_level].limit_min_cpu;
-       fault_around_bytes = levels[new_level].fault_around_bytes;
-
-       res->rescon.prev_level = curr_level;
-       res->rescon.curr_level = new_level;
+       /*
+        * Multiple PASS modules can require the change of h/w resource by
+        * using pass_level at the same time. It means RESCON (Resource
+        * Controller) have to adjust the values of h/w resource among
+        * the required pass_levels.
+        */
+       adjusted_level.limit_min_freq = MIN_INT;
+       adjusted_level.limit_max_freq = MAX_INT;
+       adjusted_level.limit_min_cpu = MIN_INT;
+       adjusted_level.limit_max_cpu = MAX_INT;
+       adjusted_level.fault_around_bytes = MIN_INT;
+
+       /* Adjust with pass_level */
+       if (res->rescon.curr_level >= 0)
+               rescon_adjust_level(&adjusted_level, &levels[rescon->curr_level]);
+
+       /* Adjust with scenario pass_level */
+       g_mutex_lock(&rescon->scenario_level_mutex);
+       while (list != NULL) {
+               i = GPOINTER_TO_INT(list->data);
+               list = g_list_next(list);
+
+               if (i < 0)
+                       continue;
+               rescon_adjust_level(&adjusted_level, &scenario_levels[i]);
+       }
+       g_mutex_unlock(&rescon->scenario_level_mutex);
+
+       switch (res_type) {
+       case PASS_RESOURCE_CPU_ID:
+               limit_min_cpu = adjusted_level.limit_min_cpu;
+               limit_max_cpu = adjusted_level.limit_max_cpu;
+               /* fall through */
+       case PASS_RESOURCE_GPU_ID:
+       case PASS_RESOURCE_BUS_ID:
+               limit_max_freq = adjusted_level.limit_max_freq;
+               limit_min_freq = adjusted_level.limit_min_freq;
+               break;
+       case PASS_RESOURCE_MEMORY_ID:
+               fault_around_bytes = adjusted_level.fault_around_bytes;
+               break;
+       case PASS_RESOURCE_NONSTANDARD_ID:
+               break;
+       default:
+               return -EINVAL;
+       }
 
        /* Turn on/off CPUs according to the required number of online CPU */
        if (limit_min_cpu >= 0 && limit_max_cpu >= 0) {
@@ -75,31 +179,78 @@ static int rescon_set_level(struct pass_resource *res, int new_level)
                if (limit_min_cpu > limit_max_cpu)
                        limit_min_cpu = limit_max_cpu;
 
-               pass_set_online_min_num(res, limit_min_cpu);
-               pass_set_online_max_num(res, limit_max_cpu);
+               ret = pass_set_online_min_num(res, limit_min_cpu);
+               if (ret == -EPERM || ret == -ENODEV) {
+                       /*
+                        * If -EPERM, function is not supported according to
+                        * h/w resource type. And if -ENODEV, function is not
+                        * implemented on HAL package. It means that this
+                        * function is not necessary on two error case
+                        * when calling the HAL functions.
+                        */
+                       ;
+               } else if (ret < 0) {
+                       _W("failed to set the minimum number of cpu(%d) of %s",
+                                               limit_min_cpu,
+                                               res->config_data.res_name);
+                       failed = 1;
+               }
+
+               ret = pass_set_online_max_num(res, limit_max_cpu);
+               if (ret == -EPERM || ret == -ENODEV) {
+                       ;
+               } else if (ret < 0) {
+                       _W("failed to set the maximum number of cpu(%d) of %s",
+                                               limit_max_cpu,
+                                               res->config_data.res_name);
+                       failed = 1;
+               }
 
-               for (i = 0; i < limit_max_cpu; i++)
-                       pass_set_online_state(res, res->config_data.cpu + i,
+               for (i = 0; i < limit_max_cpu; i++) {
+                       ret = pass_set_online_state(res,
+                                               res->config_data.cpu + i,
                                                (i < limit_min_cpu) ? 1 : 0);
+                       if (ret == -EPERM || ret == -ENODEV) {
+                               ;
+                       } else if (ret < 0) {
+                               _W("failed to turn %s of cpu%d of %s",
+                                       (i < limit_min_cpu) ? "on" : "off",
+                                       res->config_data.cpu + i,
+                                       res->config_data.res_name);
+                               failed = 1;
+                       }
+               }
+
        }
 
-       /* Set maximum frequency */
-       if (limit_max_freq > 0) {
-               ret = pass_set_max_freq(res, limit_max_freq);
-               if (ret < 0) {
-                       _E("cannot set the maximum frequency of %s",
+       /* Set minimum and maximum frequency */
+       if (limit_max_freq > 0 && limit_min_freq > 0) {
+               int curr_min_freq = pass_get_min_freq(res);
+               int curr_max_freq = pass_get_max_freq(res);
+               int ret_min;
+               int ret_max;
+
+               if ((limit_max_freq > curr_min_freq)
+                               && (limit_min_freq > curr_max_freq)) {
+                       ret_max = pass_set_max_freq(res, limit_max_freq);
+                       ret_min = pass_set_min_freq(res, limit_min_freq);
+               } else {
+                       ret_min = pass_set_min_freq(res, limit_min_freq);
+                       ret_max = pass_set_max_freq(res, limit_max_freq);
+               }
+
+               if (ret_max < 0) {
+                       _W("failed to set the maximum frequency(%d) of %s",
+                                               limit_max_freq,
                                                res->config_data.res_name);
-                       return -EINVAL;
+                       failed = 1;
                }
-       }
 
-       /* Set minimum frequency */
-       if (limit_min_freq > 0) {
-               ret = pass_set_min_freq(res, limit_min_freq);
-               if (ret < 0) {
-                       _E("cannot set the minimum frequency of %s",
+               if (ret_min < 0) {
+                       _W("failed to set the minimum frequency(%d) of %s",
+                                               limit_min_freq,
                                                res->config_data.res_name);
-                       return -EINVAL;
+                       failed = 1;
                }
        }
 
@@ -107,26 +258,67 @@ static int rescon_set_level(struct pass_resource *res, int new_level)
        if (fault_around_bytes > 0) {
                ret = pass_set_fault_around_bytes(res, fault_around_bytes);
                if (ret < 0) {
-                       _E("cannot set the fault_around_bytes of %s",
+                       _W("failed to set the fault_around_bytes(%d) of %s",
+                                               fault_around_bytes,
                                                res->config_data.res_name);
-                       return -EINVAL;
+                       failed = 1;
                }
        }
 
-       /*
-       _I("[PASS %s] Level %4s '%d->%d' : 'max %d | min %d'Hz/'%d'Core\n",
-               res->config_data.res_name,
-               (curr_level > new_level ? "DOWN" : "UP"),
-               curr_level, new_level,
-               limit_max_freq, limit_min_freq, limit_min_cpu);
-       */
+       if (!failed)
+               rescon_print_level(res, &adjusted_level);
+       else
+               _W("failed to update %s h/w resource",
+                               res->config_data.res_name);
 
        return 0;
 };
 
+static void rescon_set_scenario_level(struct pass_resource *res,
+                                       int scenario_level)
+{
+       struct pass_rescon *rescon = &res->rescon;
+
+       if (scenario_level < 0)
+               return;
+
+       g_mutex_lock(&rescon->scenario_level_mutex);
+       rescon->scenario_level_list = g_list_append(rescon->scenario_level_list,
+                                       GINT_TO_POINTER(scenario_level));
+
+       g_mutex_unlock(&rescon->scenario_level_mutex);
+}
+
+static void rescon_unset_scenario_level(struct pass_resource *res,
+                                       int scenario_level)
+{
+       struct pass_rescon *rescon = &res->rescon;
+
+       if (scenario_level < 0)
+               return;
+
+       g_mutex_lock(&rescon->scenario_level_mutex);
+       rescon->scenario_level_list = g_list_remove(rescon->scenario_level_list,
+                                       GINT_TO_POINTER(scenario_level));
+       g_mutex_unlock(&rescon->scenario_level_mutex);
+}
+
+/*
+ * @brief      Update pass_level with sync by all PASS_MODULE_*
+ * @param      [in] res Instance of struct pass_resource
+ * @param      [in] scenario_level Index of scenario level
+ * @scenario_level: the scenario level
+ */
+int pass_rescon_sync(struct pass_resource *res)
+{
+       if (!res)
+               return -EINVAL;
+
+       return rescon_update(res);
+}
+
 /**
- * @brief      Adjust the demanded pass_level by using the supported minimum
- *             and maximum pass_level.
+ * @brief      Set pass_level by PASS_MODULE_CPUHP
  * @param      [in] res Instance of h/w resource
  * @param      [in] new_level Index of demanded pass_level
  * @return     @c 0 on success, otherwise error value
@@ -136,6 +328,9 @@ int pass_rescon_set_level(struct pass_resource *res, int new_level)
        if (!res)
                return -EINVAL;
 
+       if (new_level < 0)
+               return 0;
+
        if (new_level > res->rescon.max_level)
                new_level = res->rescon.max_level;
 
@@ -145,25 +340,120 @@ int pass_rescon_set_level(struct pass_resource *res, int new_level)
        if (new_level == res->rescon.curr_level)
                return 0;
 
-       return rescon_set_level(res, new_level);
+       res->rescon.prev_level = res->rescon.curr_level;
+       res->rescon.curr_level = new_level;
+
+       return 0;
 };
 
 /**
- * @brief      Change the range of pass_level like the minimum and maximum
- *             pass_level.
+ * @brief      Set pass_level with sync by PASS_MODULE_CPUHP
  * @param      [in] res Instance of h/w resource
- * @param      [in] new_level Demanded pass_level
- * @param      [in] min_level Minimum pass_level
- * @param      [in] max_level Maximum pass_level
- * @param      [in] data PMQoS data containing the scenario name and lock state
+ * @param      [in] new_level Index of demanded pass_level
  * @return     @c 0 on success, otherwise error value
  */
-int pass_rescon_set_level_scope(struct pass_resource *res, int new_level,
-                               int min_level, int max_level, void *data)
+int pass_rescon_set_level_sync(struct pass_resource *res, int new_level)
 {
        if (!res)
                return -EINVAL;
 
+       if (new_level < 0)
+               return 0;
+
+       if (new_level > res->rescon.max_level)
+               new_level = res->rescon.max_level;
+
+       if (new_level < res->rescon.min_level)
+               new_level = res->rescon.min_level;
+
+       if (new_level == res->rescon.curr_level)
+               return 0;
+
+       res->rescon.prev_level = res->rescon.curr_level;
+       res->rescon.curr_level = new_level;
+
+       return rescon_update(res);
+};
+
+/**
+ * @brief      Set scenario pass_level with sync by PASS_MODULE_(PMQOS|THERMAL).
+ * @param      [in] res Instance of h/w resource
+ * @param      [in] scenario_level Index of demanded pass_level
+ * @return     @c 0 on success, otherwise error value
+ */
+int pass_rescon_set_scenario_level(struct pass_resource *res,
+                                       int scenario_level)
+{
+       if (!res)
+               return -EINVAL;
+
+       rescon_set_scenario_level(res, scenario_level);
+
+       return 0;
+}
+
+/*
+ * @brief      Unset the scenario pass_level by PASS_MODULE_(PMQOS|THERMAL).
+ * @param      [in] res Instance of struct pass_resource
+ * @param      [in] scenario_level Index of scenario level
+ * @scenario_level: the scenario level
+ */
+int pass_rescon_unset_scenario_level(struct pass_resource *res,
+                                       int scenario_level)
+{
+       if (!res)
+               return -EINVAL;
+
+       rescon_unset_scenario_level(res, scenario_level);
+
+       return 0;
+}
+
+/**
+ * @brief      Set scenario pass_level with sync by PASS_MODULE_(PMQOS|THERMAL).
+ * @param      [in] res Instance of h/w resource
+ * @param      [in] scenario_level Index of demanded pass_level
+ * @return     @c 0 on success, otherwise error value
+ */
+int pass_rescon_set_scenario_level_sync(struct pass_resource *res,
+                                       int scenario_level)
+{
+       if (!res)
+               return -EINVAL;
+
+       rescon_set_scenario_level(res, scenario_level);
+
+       return rescon_update(res);
+}
+
+/*
+ * @brief      Unset the scenario pass_level by PASS_MODULE_(PMQOS|THERMAL).
+ * @param      [in] res Instance of struct pass_resource
+ * @param      [in] scenario_level Index of scenario level
+ * @scenario_level: the scenario level
+ */
+int pass_rescon_unset_scenario_level_sync(struct pass_resource *res,
+                                       int scenario_level)
+{
+       if (!res)
+               return -EINVAL;
+
+       rescon_unset_scenario_level(res, scenario_level);
+
+       return rescon_update(res);
+}
+
+/*
+ * @brief      Deprecated function to support backward compatibility with sync.
+ *             Set up the scenario pass_level by PASS_MODULE_PMQOS with data.
+ * @param      [in] res Instance of struct pass_resource
+ * @param      [in] scenario_level Index of scenario level
+ * @param      [in] data PMQoS data containing the scenario name and lock state
+ * @return     @c 0 on success, otherwise error value
+ */
+int pass_rescon_set_scenario_level_sync_with_data(struct pass_resource *res,
+                                       int scenario_level, void *data)
+{
        /*
         * FIXME: PMQoS core sends the raw data to the HAL in order to
         * support the backwards compatibility. This function call will
@@ -172,23 +462,33 @@ int pass_rescon_set_level_scope(struct pass_resource *res, int new_level,
        if (data)
                pass_set_pmqos_data(res, data);
 
-       if (min_level > max_level) {
-               _E("min_level(%d) have to be smaller than max_level(%d)\n",
-                                               min_level, max_level);
-               return -EINVAL;
-       }
+       rescon_set_scenario_level(res, scenario_level);
 
-       if (min_level == res->rescon.min_level
-                       && max_level == res->rescon.max_level)
-               return 0;
+       return rescon_update(res);
+}
 
-       /* Change minimum/maximum pass level */
-       res->rescon.min_level = min_level;
-       res->rescon.max_level = max_level;
+/*
+ * @brief      Deprecated function to support backward compatibility with sync.
+ *             Unset the scenario pass_level by PASS_MODULE_PMQOS with data.
+ * @param      [in] res Insstance of struct pass_resource
+ * @param      [in] scenario_level Index of scenario level
+ * @param      [in] data PMQoS data containing the scenario name and lock state
+ * @return     @c 0 on success, otherwise error value
+ */
+int pass_rescon_unset_scenario_level_sync_with_data(struct pass_resource *res,
+                                       int scenario_level, void *data)
+{
+       /*
+        * FIXME: PMQoS core sends the raw data to the HAL in order to
+        * support the backwards compatibility. This function call will
+        * be removed after finding the proper method.
+        */
+       if (data)
+               pass_set_pmqos_data(res, data);
 
-       pass_rescon_set_level(res, new_level);
+       rescon_unset_scenario_level(res, scenario_level);
 
-       return 0;
+       return rescon_update(res);
 }
 
 /**
@@ -207,8 +507,8 @@ int pass_rescon_init(struct pass_resource *res)
        rescon = &res->rescon;
 
        /* Initialize the variables of resource-controller */
-       rescon->curr_level = 0;
-       rescon->prev_level = 0;
+       rescon->curr_level = -1;
+       rescon->prev_level = -1;
 
        if (!rescon->min_level)
                rescon->min_level = 0;
@@ -218,10 +518,9 @@ int pass_rescon_init(struct pass_resource *res)
                rescon->max_level = res->config_data.num_levels - 1;
        res->config_data.default_max_level = rescon->max_level;
 
-       if (!rescon->init_level)
-               rescon->init_level = rescon->min_level;
-       else if (rescon->init_level > rescon->max_level)
-               rescon->init_level = rescon->max_level;
+       /* Initialize g_list of scenario_level */
+       rescon->scenario_level_list = NULL;
+       g_mutex_init(&rescon->scenario_level_mutex);
 
        /*
         * Save the current data of h/w resource. The saved data
@@ -234,22 +533,33 @@ int pass_rescon_init(struct pass_resource *res)
                return ret;
        }
 
-       /* Set initial level according to init_level from configuration */
-       ret = rescon_set_level(res, rescon->init_level);
+       /*
+        * Set initial pass level when starting pass
+        * - default pass level according to res->init_level
+        */
+       ret = pass_rescon_set_level(res, rescon->init_level);
        if (ret < 0) {
                _E("failed to set level%d\n", rescon->init_level);
                return ret;
        }
 
        /*
-        * Set default pass level when starting pass
-        * - default pass level according to res->init_level
+        * Set initial scenario pass_level when starting pass
+        * - default pass level according to res->init_scenario_level
         */
-       res->rescon.curr_level = -1;
-       if (res->rescon.init_level > res->rescon.max_level)
-               res->rescon.init_level = res->rescon.max_level;
+       ret = pass_rescon_set_scenario_level(res, rescon->init_scenario_level);
+       if (ret < 0) {
+               _E("failed to set scenario level%d\n",
+                                       rescon->init_scenario_level);
+               return ret;
+       }
 
-       pass_rescon_set_level(res, res->rescon.init_level);
+       ret = pass_rescon_sync(res);
+       if (ret < 0) {
+               _E("failed to synchronize of '%s' resource\n",
+                               res->config_data.res_name);
+               return ret;
+       }
 
        rescon->state = PASS_ON;
 
@@ -284,6 +594,11 @@ int pass_rescon_exit(struct pass_resource *res)
        rescon->max_level = 0;
        rescon->init_level = 0;
 
+       /* Free g_list of scenario_level */
+       g_list_free(rescon->scenario_level_list);
+       rescon->scenario_level_list = NULL;
+       g_mutex_clear(&rescon->scenario_level_mutex);
+
        rescon->state = PASS_OFF;
 
        return ret;
index 383c8c6ed77db3db91573d5d6459d738ac32760e..8227edf45930323935852bc9d37418373f925c94 100644 (file)
 #ifndef __PASS_RESCON__
 #define __PASS_RESCON__
 
+int pass_rescon_sync(struct pass_resource *res);
+
+int pass_rescon_set_level_sync(struct pass_resource *res, int new_level);
+int pass_rescon_set_scenario_level_sync(struct pass_resource *res,
+                                       int scenario_level);
+int pass_rescon_unset_scenario_level_sync(struct pass_resource *res,
+                                       int scenario_level);
+
 int pass_rescon_set_level(struct pass_resource *res, int new_level);
-int pass_rescon_set_level_scope(struct pass_resource *res, int new_level,
-                               int min_level, int max_level, void *data);
+int pass_rescon_set_scenario_level(struct pass_resource *res,
+                                       int scenario_level);
+int pass_rescon_unset_scenario_level(struct pass_resource *res,
+                                       int scenario_level);
+
+/*
+ * Following APIs are deprecated. These functions are provided
+ * for keeping the compatibility with legacy feature.
+ */
+int pass_rescon_set_scenario_level_sync_with_data(struct pass_resource *res,
+                                       int scenario_level, void *data);
+int pass_rescon_unset_scenario_level_sync_with_data(struct pass_resource *res,
+                                       int scenario_level, void *data);
 
 #endif /* __PASS_RESCON__ */
index 22ced5b69b32e099499afce878c890bcdd9f483a..a88c61c6b6175e2ef2726af4748749bb9391bb23 100644 (file)
@@ -261,23 +261,41 @@ struct pass_rescon {
        enum pass_state state;
 
        /**
-        * Initial level when initializing h/w resource by resource controller
+        * Initial level when initializing h/w resource by resource controller.
+        * If value is -1, it is not initialized by parser.
         */
        int init_level;
-       /** Current level controlled by resource controller */
-       unsigned int curr_level;
-       /** Previous level controlled by resource controller */
-       unsigned int prev_level;
-       /** Available minimum level controlled by resource controller */
-       unsigned int min_level;
-       /** Available maximum level controlled by resource controller */
-       unsigned int max_level;
+       /**
+        * Current level controlled by resource controller.
+        * If value is -1, it is not initialized by parser.
+        */
+       int curr_level;
+       /**
+        * Previous level controlled by resource controller.
+        * If value is -1, it is not initialized by parser.
+        */
+       int prev_level;
+       /**
+        * Available minimum level controlled by resource controller.
+        * If value is -1, it is not initialized by parser.
+        */
+       int min_level;
+       /**
+        * Available maximum level controlled by resource controller.
+        * If value is -1, it is not initialized by parser.
+        */
+       int max_level;
 
        /**
         * Initial level when initializing h/w resource by resource controller.
         * If value is -1, it is not initialized by parser.
         */
        int init_scenario_level;
+
+       /** Ondemanded scenario list */
+       GList *scenario_level_list;
+       /** Mutex of ondemanded scenario list */
+       GMutex scenario_level_mutex;
 };
 
 /**