#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) {
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;
}
}
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
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.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
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);
}
/**
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;
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
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;
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;