[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC PATCH 11/13] cpufreq: add xen-cpufreq driver



This driver uses Dom0 to change frequencies on CPUs

Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx>
---
 xen/Rules.mk                      |   1 +
 xen/drivers/cpufreq/Makefile      |   1 +
 xen/drivers/cpufreq/xen-cpufreq.c | 241 ++++++++++++++++++++++++++++++++++++++
 xen/include/public/xen.h          |   1 +
 4 files changed, 244 insertions(+)
 create mode 100644 xen/drivers/cpufreq/xen-cpufreq.c

diff --git a/xen/Rules.mk b/xen/Rules.mk
index 23045bb..e3bc77f 100644
--- a/xen/Rules.mk
+++ b/xen/Rules.mk
@@ -56,6 +56,7 @@ CFLAGS-$(perfc_arrays)  += -DPERF_ARRAYS
 CFLAGS-$(lock_profile)  += -DLOCK_PROFILE
 CFLAGS-$(HAS_ACPI)      += -DHAS_ACPI
 CFLAGS-$(HAS_CPUFREQ)   += -DHAS_CPUFREQ
+CFLAGS-$(HAS_XEN_CPUFREQ) += -DHAS_XEN_CPUFREQ
 CFLAGS-$(HAS_PM)        += -DHAS_PM
 CFLAGS-$(HAS_GDBSX)     += -DHAS_GDBSX
 CFLAGS-$(HAS_PASSTHROUGH) += -DHAS_PASSTHROUGH
diff --git a/xen/drivers/cpufreq/Makefile b/xen/drivers/cpufreq/Makefile
index b87d127..673edba 100644
--- a/xen/drivers/cpufreq/Makefile
+++ b/xen/drivers/cpufreq/Makefile
@@ -2,3 +2,4 @@ obj-y += cpufreq.o
 obj-y += cpufreq_ondemand.o
 obj-y += cpufreq_misc_governors.o
 obj-y += utility.o
+obj-$(HAS_XEN_CPUFREQ) += xen-cpufreq.o
diff --git a/xen/drivers/cpufreq/xen-cpufreq.c 
b/xen/drivers/cpufreq/xen-cpufreq.c
new file mode 100644
index 0000000..854955b
--- /dev/null
+++ b/xen/drivers/cpufreq/xen-cpufreq.c
@@ -0,0 +1,241 @@
+/*
+ *  Copyright (C) 2014 GlobalLogic Inc.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <xen/types.h>
+#include <xen/errno.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/irq.h>
+#include <xen/spinlock.h>
+#include <asm/current.h>
+#include <cpufreq/cpufreq.h>
+
+struct acpi_cpufreq_data *cpufreq_drv_data[NR_CPUS];
+static DEFINE_SPINLOCK(sysctl_cpufreq_lock);
+
+struct sysctl_cpufreq_data {
+    uint32_t cpu;
+    uint32_t freq;
+    uint32_t relation;
+    int32_t result;
+};
+
+static struct sysctl_cpufreq_data sysctl_cpufreq_data;
+
+int cpufreq_cpu_init(unsigned int cpuid)
+{
+    return cpufreq_add_cpu(cpuid);
+}
+
+static void notify_cpufreq_domains(void)
+{
+    send_global_virq(VIRQ_CPUFREQ);
+}
+
+static int xen_cpufreq_verify(struct cpufreq_policy *policy)
+{
+    struct acpi_cpufreq_data *data;
+    struct processor_performance *perf;
+
+    if ( !policy || !(data = cpufreq_drv_data[policy->cpu]) ||
+         !processor_pminfo[policy->cpu] )
+        return -EINVAL;
+
+    perf = &processor_pminfo[policy->cpu]->perf;
+
+    cpufreq_verify_within_limits(policy, 0,
+        perf->states[perf->platform_limit].core_frequency * 1000);
+
+    return cpufreq_frequency_table_verify(policy, data->freq_table);
+}
+
+static int xen_cpufreq_target(struct cpufreq_policy *policy,
+                               unsigned int target_freq, unsigned int relation)
+{
+    struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
+    struct processor_performance *perf;
+    struct cpufreq_freqs freqs;
+    cpumask_t online_policy_cpus;
+    unsigned int next_state = 0; /* Index into freq_table */
+    unsigned int next_perf_state = 0; /* Index into perf table */
+    unsigned int j;
+    int ret = 0;
+
+    if ( unlikely(data == NULL ||
+         data->acpi_data == NULL || data->freq_table == NULL) )
+    {
+        return -ENODEV;
+    }
+
+    perf = data->acpi_data;
+    ret = cpufreq_frequency_table_target(policy,
+                                            data->freq_table,
+                                            target_freq,
+                                            relation, &next_state);
+    if ( unlikely(ret) )
+        return -ENODEV;
+
+    cpumask_and(&online_policy_cpus, &cpu_online_map, policy->cpus);
+
+    next_perf_state = data->freq_table[next_state].index;
+    if ( perf->state == next_perf_state )
+    {
+        if ( unlikely(policy->resume) )
+            policy->resume = 0;
+        else
+            return 0;
+    }
+
+    freqs.old = perf->states[perf->state].core_frequency * 1000;
+    freqs.new = data->freq_table[next_state].frequency;
+
+    /* Do send cmd for Dom0 */
+    spin_lock(&sysctl_cpufreq_lock);
+    /* return previous result */
+    ret = sysctl_cpufreq_data.result;
+
+    sysctl_cpufreq_data.cpu = policy->cpu;
+    sysctl_cpufreq_data.freq = freqs.new;
+    sysctl_cpufreq_data.relation = (uint32_t)relation;
+    spin_unlock(&sysctl_cpufreq_lock);
+    notify_cpufreq_domains();
+
+    for_each_cpu( j, &online_policy_cpus )
+        cpufreq_statistic_update(j, perf->state, next_perf_state);
+
+    perf->state = next_perf_state;
+    policy->cur = freqs.new;
+
+    return ret;
+}
+
+static int
+xen_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+    struct processor_performance *perf;
+    struct acpi_cpufreq_data *data;
+    unsigned int cpu = policy->cpu;
+    unsigned int valid_states = 0;
+    int i;
+    int ret = 0;
+
+    data = xzalloc(struct acpi_cpufreq_data);
+    if ( !data )
+        return -ENOMEM;
+
+    cpufreq_drv_data[cpu] = data;
+
+    data->acpi_data = &processor_pminfo[cpu]->perf;
+
+    perf = data->acpi_data;
+    policy->shared_type = perf->shared_type;
+
+    data->freq_table = xmalloc_array(struct cpufreq_frequency_table,
+                                    (perf->state_count+1));
+    if ( !data->freq_table )
+    {
+        ret = -ENOMEM;
+        goto err_unreg;
+    }
+
+    /* detect transition latency */
+    policy->cpuinfo.transition_latency = 0;
+    for ( i=0; i<perf->state_count; i++ )
+    {
+        if ( (perf->states[i].transition_latency * 1000) >
+             policy->cpuinfo.transition_latency )
+            policy->cpuinfo.transition_latency =
+                perf->states[i].transition_latency * 1000;
+    }
+
+    policy->governor = cpufreq_opt_governor ? : CPUFREQ_DEFAULT_GOVERNOR;
+
+    /* table init */
+    for ( i=0; i<perf->state_count; i++ )
+    {
+        if ( i>0 && perf->states[i].core_frequency >=
+            data->freq_table[valid_states-1].frequency / 1000 )
+            continue;
+
+        data->freq_table[valid_states].index = i;
+        data->freq_table[valid_states].frequency =
+            perf->states[i].core_frequency * 1000;
+        valid_states++;
+    }
+    data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
+    perf->state = 0;
+
+    ret = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
+    if ( ret )
+        goto err_freqfree;
+
+
+    /*
+     * the first call to ->target() should result in us actually
+     * send command to the Dom0 to set frequency.
+     */
+    policy->resume = 1;
+
+    /* Set the minimal frequency */
+    return xen_cpufreq_target(policy, policy->min, CPUFREQ_RELATION_L);
+
+err_freqfree:
+    xfree(data->freq_table);
+err_unreg:
+    xfree(data);
+    cpufreq_drv_data[cpu] = NULL;
+
+    return ret;
+}
+
+static int xen_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+    struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
+
+    if ( data )
+    {
+        cpufreq_drv_data[policy->cpu] = NULL;
+        xfree(data->freq_table);
+        xfree(data);
+    }
+
+    return 0;
+}
+
+static struct cpufreq_driver xen_cpufreq_driver = {
+    .name   = "xen-cpufreq",
+    .verify = xen_cpufreq_verify,
+    .target = xen_cpufreq_target,
+    .init   = xen_cpufreq_cpu_init,
+    .exit   = xen_cpufreq_cpu_exit,
+};
+
+int __init xen_cpufreq_driver_init(void)
+{
+    int ret = 0;
+
+    if ( cpufreq_controller == FREQCTL_xen )
+        ret = cpufreq_register_driver(&xen_cpufreq_driver);
+
+    return ret;
+}
+
+__initcall(xen_cpufreq_driver_init);
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index 8c5697e..86dd479 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -160,6 +160,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_ulong_t);
 #define VIRQ_MEM_EVENT  10 /* G. (DOM0) A memory event has occured           */
 #define VIRQ_XC_RESERVED 11 /* G. Reserved for XenClient                     */
 #define VIRQ_ENOMEM     12 /* G. (DOM0) Low on heap memory       */
+#define VIRQ_CPUFREQ    13 /* G. (DOM0) Notify xen-cpufreq driver.           */
 
 /* Architecture-specific VIRQ definitions. */
 #define VIRQ_ARCH_0    16
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.