From patchwork Wed Jun 1 10:39:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 9147155 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 46FC360761 for ; Wed, 1 Jun 2016 10:40:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3A80925EF7 for ; Wed, 1 Jun 2016 10:40:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2F6F32699B; Wed, 1 Jun 2016 10:40:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E916E25EF7 for ; Wed, 1 Jun 2016 10:40:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757299AbcFAKkL (ORCPT ); Wed, 1 Jun 2016 06:40:11 -0400 Received: from mail-pf0-f169.google.com ([209.85.192.169]:34961 "EHLO mail-pf0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757154AbcFAKkG (ORCPT ); Wed, 1 Jun 2016 06:40:06 -0400 Received: by mail-pf0-f169.google.com with SMTP id g64so13013243pfb.2 for ; Wed, 01 Jun 2016 03:40:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=XNExX5SGF3/cccckFzy4n6nS1OdBYrWTSVpFkrbHbuk=; b=ijGuSxeoD7j/Diva68RbAFM91YZUAoPBvKE+DOdfRyw6VtEa8eo4zOwRAysaF/fe5X 9FUc0uMIoFaAvdNFwwS6QAkeA2wV7oTc1rTtsUl/l6cd5nzNmhTQ+4ZBjlKhEeN85MW6 oBLqG2gNczF/GbG2n+60ehXiMeDMgEWC/qOJY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=XNExX5SGF3/cccckFzy4n6nS1OdBYrWTSVpFkrbHbuk=; b=TKZVRBxYqntMV8TiZKBX60s+uH64SsMh655XlMfKe4QnpWfVHmluVs+6mRNCKRwlDG cKVpVJLTWRuA1D5k/nyB/3+bh1RMcT/oGgb9T30ES7PybX0ka5LQmKadz9oPYbdG+dLZ DhxNy+w7p5mIgYTrd+ySlPUi3Z5OTuniDorzNOR2ZyuH+Ih8OencpWCrpRu/itcYp72z AXuIWtmrL3vwKI+DZU7Zfl7zIbkZj8w6evq5cNGB8GO6mrcOLW4XewAWKiCVTayVgv+Y bWln+N4MrHntO34psKCzyahPmEhsMsI73xdvzKBq6GQOm8t5BjOleS0qslarWYvUV6F7 bT9w== X-Gm-Message-State: ALyK8tJsLFDTiwJ/bY5kxhAP5OlOieiGIIjMiCs69LoC/4KHaSowNecgs22c3gBl+NP5g71F X-Received: by 10.98.94.135 with SMTP id s129mr7310504pfb.43.1464777605767; Wed, 01 Jun 2016 03:40:05 -0700 (PDT) Received: from localhost ([122.167.174.248]) by smtp.gmail.com with ESMTPSA id o13sm3736606pfj.46.2016.06.01.03.40.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 01 Jun 2016 03:40:05 -0700 (PDT) From: Viresh Kumar To: Rafael Wysocki , Viresh Kumar Cc: linaro-kernel@lists.linaro.org, linux-pm@vger.kernel.org, smuckle@linaro.org, linux-kernel@vger.kernel.org Subject: [PATCH V2 2/2] cpufreq: Optimize cpufreq_frequency_table_target() Date: Wed, 1 Jun 2016 16:09:55 +0530 Message-Id: <120ed8a873b6df2ccc9406eeec8f8f74e5f9b0d5.1464777376.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.7.1.410.g6faf27b In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP cpufreq core keeps another table of sorted frequencies now and that can be used to find a match quickly, instead of traversing the unsorted list in an inefficient way. Create helper routines for separate relation types to optimize it further. Ideally the new routine cpufreq_find_target_index() is required to be exported, but s3c24xx was abusing the earlier API and have to be supported for now. Added a FIXME for it. Tested on Exynos board with both ondemand and schedutil governor and confirmed with help of various print messages that we are eventually switching to the desired frequency based on a target frequency. Signed-off-by: Viresh Kumar --- drivers/cpufreq/acpi-cpufreq.c | 15 ++-- drivers/cpufreq/freq_table.c | 180 ++++++++++++++++++++++---------------- drivers/cpufreq/s3c24xx-cpufreq.c | 13 ++- include/linux/cpufreq.h | 20 ++++- 4 files changed, 134 insertions(+), 94 deletions(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 32a15052f363..4f9f7504a17c 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -468,20 +468,15 @@ unsigned int acpi_cpufreq_fast_switch(struct cpufreq_policy *policy, struct acpi_cpufreq_data *data = policy->driver_data; struct acpi_processor_performance *perf; struct cpufreq_frequency_table *entry; - unsigned int next_perf_state, next_freq, freq; + unsigned int next_perf_state, next_freq, index; /* * Find the closest frequency above target_freq. - * - * The table is sorted in the reverse order with respect to the - * frequency and all of the entries are valid (see the initialization). */ - entry = policy->freq_table; - do { - entry++; - freq = entry->frequency; - } while (freq >= target_freq && freq != CPUFREQ_TABLE_END); - entry--; + index = cpufreq_frequency_table_target(policy, target_freq, + CPUFREQ_RELATION_L); + + entry = &policy->freq_table[index]; next_freq = entry->frequency; next_perf_state = entry->driver_data; diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index d536136c8e1f..15c4a2462c68 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -114,99 +114,131 @@ int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify); -int cpufreq_frequency_table_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) +static int cpufreq_find_target_index_l(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq) { - struct cpufreq_frequency_table optimal = { - .driver_data = ~0, - .frequency = 0, - }; - struct cpufreq_frequency_table suboptimal = { - .driver_data = ~0, - .frequency = 0, - }; - struct cpufreq_frequency_table *pos; - struct cpufreq_frequency_table *table = policy->freq_table; - unsigned int freq, diff, i = 0; - int index; + struct cpufreq_frequency_table *pos, *best = NULL; + unsigned int freq; - pr_debug("request for target %u kHz (relation: %u) for cpu %u\n", - target_freq, relation, policy->cpu); + cpufreq_for_each_valid_entry(pos, table) { + freq = pos->frequency; - switch (relation) { - case CPUFREQ_RELATION_H: - suboptimal.frequency = ~0; - break; - case CPUFREQ_RELATION_L: - case CPUFREQ_RELATION_C: - optimal.frequency = ~0; + if ((freq < policy->min) || (freq > policy->max)) + continue; + + if (freq >= target_freq) + return pos - table; + + best = pos; + } + + if (best) + return best - table; + + return -EINVAL; +} + +static int cpufreq_find_target_index_h(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq) +{ + struct cpufreq_frequency_table *pos, *best = NULL; + unsigned int freq; + + cpufreq_for_each_valid_entry(pos, table) { + freq = pos->frequency; + + if ((freq < policy->min) || (freq > policy->max)) + continue; + + if (freq == target_freq) + return pos - table; + + if (freq < target_freq) { + best = pos; + continue; + } + + /* No freq found below target_freq */ + if (!best) + best = pos; break; } + if (best) + return best - table; + + return -EINVAL; +} + +static int cpufreq_find_target_index_c(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq) +{ + struct cpufreq_frequency_table *pos, *best = NULL; + unsigned int freq; + cpufreq_for_each_valid_entry(pos, table) { freq = pos->frequency; - i = pos - table; if ((freq < policy->min) || (freq > policy->max)) continue; - if (freq == target_freq) { - optimal.driver_data = i; - break; + + if (freq == target_freq) + return pos - table; + + if (freq < target_freq) { + best = pos; + continue; } - switch (relation) { - case CPUFREQ_RELATION_H: - if (freq < target_freq) { - if (freq >= optimal.frequency) { - optimal.frequency = freq; - optimal.driver_data = i; - } - } else { - if (freq <= suboptimal.frequency) { - suboptimal.frequency = freq; - suboptimal.driver_data = i; - } - } - break; - case CPUFREQ_RELATION_L: - if (freq > target_freq) { - if (freq <= optimal.frequency) { - optimal.frequency = freq; - optimal.driver_data = i; - } - } else { - if (freq >= suboptimal.frequency) { - suboptimal.frequency = freq; - suboptimal.driver_data = i; - } - } - break; - case CPUFREQ_RELATION_C: - diff = abs(freq - target_freq); - if (diff < optimal.frequency || - (diff == optimal.frequency && - freq > table[optimal.driver_data].frequency)) { - optimal.frequency = diff; - optimal.driver_data = i; - } + + /* No freq found below target_freq */ + if (!best) { + best = pos; break; } + + /* Choose the closest freq */ + if (target_freq - best->frequency > freq - target_freq) + best = pos; + + break; + } + + if (best) + return best - table; + + return -EINVAL; +} + +int cpufreq_find_target_index(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, unsigned int relation) +{ + int index; + + switch (relation) { + case CPUFREQ_RELATION_L: + index = cpufreq_find_target_index_l(policy, table, target_freq); + break; + case CPUFREQ_RELATION_H: + index = cpufreq_find_target_index_h(policy, table, target_freq); + break; + case CPUFREQ_RELATION_C: + index = cpufreq_find_target_index_c(policy, table, target_freq); + break; + default: + pr_err("%s: Invalid relation: %d\n", __func__, relation); + return -EINVAL; } - if (optimal.driver_data > i) { - if (suboptimal.driver_data > i) { - WARN(1, "Invalid frequency table: %d\n", policy->cpu); - return 0; - } - index = suboptimal.driver_data; - } else - index = optimal.driver_data; + if (index == -EINVAL) + WARN(1, "Invalid frequency table: %d\n", policy->cpu); - pr_debug("target index is %u, freq is:%u kHz\n", index, - table[index].frequency); return index; } -EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); +EXPORT_SYMBOL_GPL(cpufreq_find_target_index); int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, unsigned int freq) diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 7b596fa38ad2..c9c2b4151b9f 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -318,14 +318,13 @@ static int s3c_cpufreq_target(struct cpufreq_policy *policy, tmp_policy.min = policy->min * 1000; tmp_policy.max = policy->max * 1000; tmp_policy.cpu = policy->cpu; - tmp_policy.freq_table = pll_reg; - /* cpufreq_frequency_table_target returns the index - * of the table entry, not the value of - * the table entry's index field. */ - - index = cpufreq_frequency_table_target(&tmp_policy, target_freq, - relation); + /* + * FIXME: We should be providing this freq-table to the cpufreq + * core instead of managing it ourselves here. + */ + index = cpufreq_find_target_index(&tmp_policy, pll_reg, + target_freq, relation); pll = pll_reg + index; s3c_freq_dbg("%s: target %u => %u\n", diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 5e23eed2252c..5aabec611e87 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -600,14 +600,28 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table); int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy); -int cpufreq_frequency_table_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation); +int cpufreq_find_target_index(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, unsigned int relation); int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, unsigned int freq); ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); +/* + * Search in the sorted freq table local to the core and return index of + * policy->freq_table. + */ +static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int index = cpufreq_find_target_index(policy, policy->sorted_freq_table, + target_freq, relation); + + return policy->sorted_freq_table[index].driver_data; +} + #ifdef CONFIG_CPU_FREQ int cpufreq_boost_trigger_state(int state); int cpufreq_boost_enabled(void);