From patchwork Thu Nov 9 17:09:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksandr Tyshchenko X-Patchwork-Id: 10051397 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 9144C60632 for ; Thu, 9 Nov 2017 17:13:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 725BA2B050 for ; Thu, 9 Nov 2017 17:13:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 673CC2B04F; Thu, 9 Nov 2017 17:13:46 +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=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id AF0642B041 for ; Thu, 9 Nov 2017 17:13:44 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eCqLe-0003yA-Cp; Thu, 09 Nov 2017 17:10:38 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eCqLd-0003xL-Af for xen-devel@lists.xenproject.org; Thu, 09 Nov 2017 17:10:37 +0000 Received: from [85.158.143.35] by server-3.bemta-6.messagelabs.com id BB/DF-14867-C0C840A5; Thu, 09 Nov 2017 17:10:36 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrIIsWRWlGSWpSXmKPExsVyMfS6sy53D0u UwdHPkhbft0xmcmD0OPzhCksAYxRrZl5SfkUCa8b72fNZCuZ0MlVsvvyfrYGx5wRjFyMnh5DA DEaJk1Ptuxi5OFgEXrJIXLnZDZaQEOhnldg4yxbCzpL48+AZK4SdJvH2w1NmCLtc4u2XJewQg 5QkXu/czAQySEhgNpPEmxmrwQaxCRhI7H/3BKxIBKjo3qrJYEXMIEX393wESwgLREssP7QAbC qLgKrE7t3vwGxeAWeJ+b/2s0Nsk5O4ea4TLM4p4CLx5uN1NojNzhIXXx9nm8AouICRYRWjenF qUVlqka6ZXlJRZnpGSW5iZo6uoYGZXm5qcXFiempOYlKxXnJ+7iZGYNAxAMEOxnkn/A8xSnIw KYnySlmyRAnxJeWnVGYkFmfEF5XmpBYfYpTh4FCS4OXpBsoJFqWmp1akZeYAwx8mLcHBoyTC+ 64LKM1bXJCYW5yZDpE6xRjIceHOpT9MHMc2XQaSByZcAZF7bgHJRzfuAslnM183MHNMu9raxC zEkpeflyolzmsFskcAZFBGaR7cGlhMX2KUlRLmZQQ6XIinILUoN7MEVf4VozgHo5Iwbw3IOTy ZeSVw17wCOpQJ6NBodrBDSxIRUlINjP1u7Tq5WrdKr9Vdl7Cc0Zzw9kLuO6nY8ptMkxieNWYs f8v/9030D0f+3WufFERPWKyTc/DzYjeeOz/SuBKvxaw/Y/I55t31r13rlshOZpAN5F3P+2gH6 zEJ50zJqVay/HnNCuUbFdl552ge79+dyc5749XM5uRVq2I2FqjfqmkLnDnBpEDtlxJLcUaioR ZzUXEiAP1iy+PkAgAA X-Env-Sender: olekstysh@gmail.com X-Msg-Ref: server-9.tower-21.messagelabs.com!1510247434!81163979!1 X-Originating-IP: [209.85.215.67] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 27355 invoked from network); 9 Nov 2017 17:10:35 -0000 Received: from mail-lf0-f67.google.com (HELO mail-lf0-f67.google.com) (209.85.215.67) by server-9.tower-21.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 9 Nov 2017 17:10:35 -0000 Received: by mail-lf0-f67.google.com with SMTP id r129so8037291lff.8 for ; Thu, 09 Nov 2017 09:10:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=X3EoUEDkz9C1y2aUlknk4mJYxdG5oZckOZuSH6J85PA=; b=nK/4S3dr2W/z1Y24feyME8W2OjHu2u+0zHW+x0YFuWj1a57OM63Y3buHr5akvXomEY f5XrnIRwq9I0ERccK1cuHwTf8xL5VxVawgRecWD8r7TKUQHWDx299/Mu9s9LE7Kj3qj+ H8l0tEfu+DZ6Q2/At94HfM2ZY4o0aQRgP6bRvL+jdSPYraHZ0modMmZup/Lw8vY70m/O g2/PtlBA3JAl2wN1BbS3hU6NecojutIwACraAzpR+3j6mD+v4nS7SvW3tDFTSqde+eUg Ru5CYuTYG+xh9lafgUvQV5kIY7mCOBeV4h1Xoph/CSGAkn0IbtCUlk/s7P2Vsg9ANBig dyxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=X3EoUEDkz9C1y2aUlknk4mJYxdG5oZckOZuSH6J85PA=; b=Q3jh9Hj2w0Jmao4AkpUFe9Z7MTnU7tDAAJXQF6nkiHKsGEyRC3pOez3uwL9hTLpfjb pz87daC9c4S37Aq8h20J9e3NXHbpWGjT7yTbHy54F3aniwAb4ToQ8rrh3Rpbae2I04Ol d9EibjAkHeoCW5GrBefo0A0rIRU/0+wiIKSCuJ5H2+2c7tHce3kWYMnVOBQFtMHO3BNt WBmPIlDKVf8EbpTo1iXRzQh6unEw+JRVO79kBA3gMaf/pLWOjeEtS4KlQH27bb5IPfSB ww02NspEWqFfgLxEXDi5ESN+BNG2orrhGtuGeUUknBXBT+BKWUqecEROOJuzpmcv6fUu Qulg== X-Gm-Message-State: AJaThX4ypjCrY5xq0rrkaOVbjdtwjsRp+fk2rkVWvgtmNro7KENjwjUj jbVEMp2cZPUmZA9eSVrIWjuSYQ== X-Google-Smtp-Source: ABhQp+Swh6KLbPicwx9oWDONXr4KN+U9PVe+fjde3rbZuWcBCfjAqq7HoT8JNeCZJ1ttIQbgEuOFfw== X-Received: by 10.46.23.85 with SMTP id l82mr481089lje.178.1510247434240; Thu, 09 Nov 2017 09:10:34 -0800 (PST) Received: from otyshchenko.kyiv.epam.com (ll-53.209.223.85.sovam.net.ua. [85.223.209.53]) by smtp.gmail.com with ESMTPSA id x90sm1394299ljb.86.2017.11.09.09.10.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 09 Nov 2017 09:10:33 -0800 (PST) From: Oleksandr Tyshchenko To: xen-devel@lists.xenproject.org Date: Thu, 9 Nov 2017 19:09:53 +0200 Message-Id: <1510247421-24094-4-git-send-email-olekstysh@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1510247421-24094-1-git-send-email-olekstysh@gmail.com> References: <1510247421-24094-1-git-send-email-olekstysh@gmail.com> Cc: Stefano Stabellini , Andrew Cooper , Oleksandr Dmytryshyn , Julien Grall , Oleksandr Tyshchenko , Jan Beulich Subject: [Xen-devel] [RFC PATCH 03/31] pmstat: move pmstat.c file to the xen/drivers/pm/stat.c location X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Oleksandr Dmytryshyn Cpufreq driver should be more generalizable (not ACPI-specific). Thus this file should be placed to more convenient location. This is a rebased version of the original patch: https://lists.xen.org/archives/html/xen-devel/2014-11/msg00935.html Signed-off-by: Oleksandr Dmytryshyn Signed-off-by: Oleksandr Tyshchenko CC: Jan Beulich CC: Andrew Cooper CC: Stefano Stabellini CC: Julien Grall --- MAINTAINERS | 1 + xen/arch/x86/Kconfig | 1 + xen/common/sysctl.c | 2 +- xen/drivers/Kconfig | 2 + xen/drivers/Makefile | 1 + xen/drivers/acpi/Makefile | 1 - xen/drivers/acpi/pmstat.c | 526 ---------------------------------------------- xen/drivers/pm/Kconfig | 3 + xen/drivers/pm/Makefile | 1 + xen/drivers/pm/stat.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 536 insertions(+), 528 deletions(-) delete mode 100644 xen/drivers/acpi/pmstat.c create mode 100644 xen/drivers/pm/Kconfig create mode 100644 xen/drivers/pm/Makefile create mode 100644 xen/drivers/pm/stat.c diff --git a/MAINTAINERS b/MAINTAINERS index 9794a81..87ade6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -294,6 +294,7 @@ F: xen/arch/x86/acpi/ X: xen/arch/x86/acpi/boot.c X: xen/arch/x86/acpi/lib.c F: xen/drivers/cpufreq/ +F: xen/drivers/pm/ F: xen/include/xen/cpufreq.h F: xen/include/xen/processor_perf.h diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig index 30c2769..86c8eca 100644 --- a/xen/arch/x86/Kconfig +++ b/xen/arch/x86/Kconfig @@ -23,6 +23,7 @@ config X86 select HAS_PDX select NUMA select VGA + select HAS_PM config ARCH_DEFCONFIG string diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index a6882d1..ac96347 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -171,7 +171,7 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) op->u.availheap.avail_bytes <<= PAGE_SHIFT; break; -#if defined (CONFIG_ACPI) && defined (CONFIG_HAS_CPUFREQ) +#if defined (CONFIG_HAS_PM) && defined (CONFIG_HAS_CPUFREQ) case XEN_SYSCTL_get_pmstat: ret = do_get_pm_info(&op->u.get_pmstat); break; diff --git a/xen/drivers/Kconfig b/xen/drivers/Kconfig index bc3a54f..ddaec11 100644 --- a/xen/drivers/Kconfig +++ b/xen/drivers/Kconfig @@ -12,4 +12,6 @@ source "drivers/pci/Kconfig" source "drivers/video/Kconfig" +source "drivers/pm/Kconfig" + endmenu diff --git a/xen/drivers/Makefile b/xen/drivers/Makefile index 1939180..dd0b496 100644 --- a/xen/drivers/Makefile +++ b/xen/drivers/Makefile @@ -4,3 +4,4 @@ subdir-$(CONFIG_HAS_PCI) += pci subdir-$(CONFIG_HAS_PASSTHROUGH) += passthrough subdir-$(CONFIG_ACPI) += acpi subdir-$(CONFIG_VIDEO) += video +subdir-$(CONFIG_HAS_PM) += pm diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index 444b11d..6f6470a 100644 --- a/xen/drivers/acpi/Makefile +++ b/xen/drivers/acpi/Makefile @@ -5,7 +5,6 @@ subdir-$(CONFIG_X86) += apei obj-bin-y += tables.init.o obj-$(CONFIG_NUMA) += numa.o obj-y += osl.o -obj-$(CONFIG_HAS_CPUFREQ) += pmstat.o obj-$(CONFIG_X86) += hwregs.o obj-$(CONFIG_X86) += reboot.o diff --git a/xen/drivers/acpi/pmstat.c b/xen/drivers/acpi/pmstat.c deleted file mode 100644 index 2dbde1c..0000000 --- a/xen/drivers/acpi/pmstat.c +++ /dev/null @@ -1,526 +0,0 @@ -/***************************************************************************** -# pmstat.c - Power Management statistic information (Px/Cx/Tx, etc.) -# -# Copyright (c) 2008, Liu Jinsong -# -# 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, see . -# -# The full GNU General Public License is included in this distribution in the -# file called LICENSE. -# -*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -DEFINE_PER_CPU_READ_MOSTLY(struct pm_px *, cpufreq_statistic_data); - -/* - * Get PM statistic info - */ -int do_get_pm_info(struct xen_sysctl_get_pmstat *op) -{ - int ret = 0; - const struct processor_pminfo *pmpt; - - if ( !op || (op->cpuid >= nr_cpu_ids) || !cpu_online(op->cpuid) ) - return -EINVAL; - pmpt = processor_pminfo[op->cpuid]; - - switch ( op->type & PMSTAT_CATEGORY_MASK ) - { - case PMSTAT_CX: - if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_CX) ) - return -ENODEV; - break; - case PMSTAT_PX: - if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) ) - return -ENODEV; - if ( !cpufreq_driver ) - return -ENODEV; - if ( !pmpt || !(pmpt->perf.init & XEN_PX_INIT) ) - return -EINVAL; - break; - default: - return -ENODEV; - } - - switch ( op->type ) - { - case PMSTAT_get_max_px: - { - op->u.getpx.total = pmpt->perf.state_count; - break; - } - - case PMSTAT_get_pxstat: - { - uint32_t ct; - struct pm_px *pxpt; - spinlock_t *cpufreq_statistic_lock = - &per_cpu(cpufreq_statistic_lock, op->cpuid); - - spin_lock(cpufreq_statistic_lock); - - pxpt = per_cpu(cpufreq_statistic_data, op->cpuid); - if ( !pxpt || !pxpt->u.pt || !pxpt->u.trans_pt ) - { - spin_unlock(cpufreq_statistic_lock); - return -ENODATA; - } - - pxpt->u.usable = pmpt->perf.state_count - pmpt->perf.platform_limit; - - cpufreq_residency_update(op->cpuid, pxpt->u.cur); - - ct = pmpt->perf.state_count; - if ( copy_to_guest(op->u.getpx.trans_pt, pxpt->u.trans_pt, ct*ct) ) - { - spin_unlock(cpufreq_statistic_lock); - ret = -EFAULT; - break; - } - - if ( copy_to_guest(op->u.getpx.pt, pxpt->u.pt, ct) ) - { - spin_unlock(cpufreq_statistic_lock); - ret = -EFAULT; - break; - } - - op->u.getpx.total = pxpt->u.total; - op->u.getpx.usable = pxpt->u.usable; - op->u.getpx.last = pxpt->u.last; - op->u.getpx.cur = pxpt->u.cur; - - spin_unlock(cpufreq_statistic_lock); - - break; - } - - case PMSTAT_reset_pxstat: - { - cpufreq_statistic_reset(op->cpuid); - break; - } - - case PMSTAT_get_max_cx: - { - op->u.getcx.nr = pmstat_get_cx_nr(op->cpuid); - ret = 0; - break; - } - - case PMSTAT_get_cxstat: - { - ret = pmstat_get_cx_stat(op->cpuid, &op->u.getcx); - break; - } - - case PMSTAT_reset_cxstat: - { - ret = pmstat_reset_cx_stat(op->cpuid); - break; - } - - default: - printk("not defined sub-hypercall @ do_get_pm_info\n"); - ret = -ENOSYS; - break; - } - - return ret; -} - -/* - * 1. Get PM parameter - * 2. Provide user PM control - */ -static int read_scaling_available_governors(char *scaling_available_governors, - unsigned int size) -{ - unsigned int i = 0; - struct cpufreq_governor *t; - - if ( !scaling_available_governors ) - return -EINVAL; - - list_for_each_entry(t, &cpufreq_governor_list, governor_list) - { - i += scnprintf(&scaling_available_governors[i], - CPUFREQ_NAME_LEN, "%s ", t->name); - if ( i > size ) - return -EINVAL; - } - scaling_available_governors[i-1] = '\0'; - - return 0; -} - -static int get_cpufreq_para(struct xen_sysctl_pm_op *op) -{ - uint32_t ret = 0; - const struct processor_pminfo *pmpt; - struct cpufreq_policy *policy; - uint32_t gov_num = 0; - uint32_t *affected_cpus; - uint32_t *scaling_available_frequencies; - char *scaling_available_governors; - struct list_head *pos; - uint32_t cpu, i, j = 0; - - pmpt = processor_pminfo[op->cpuid]; - policy = per_cpu(cpufreq_cpu_policy, op->cpuid); - - if ( !pmpt || !pmpt->perf.states || - !policy || !policy->governor ) - return -EINVAL; - - list_for_each(pos, &cpufreq_governor_list) - gov_num++; - - if ( (op->u.get_para.cpu_num != cpumask_weight(policy->cpus)) || - (op->u.get_para.freq_num != pmpt->perf.state_count) || - (op->u.get_para.gov_num != gov_num) ) - { - op->u.get_para.cpu_num = cpumask_weight(policy->cpus); - op->u.get_para.freq_num = pmpt->perf.state_count; - op->u.get_para.gov_num = gov_num; - return -EAGAIN; - } - - if ( !(affected_cpus = xzalloc_array(uint32_t, op->u.get_para.cpu_num)) ) - return -ENOMEM; - for_each_cpu(cpu, policy->cpus) - affected_cpus[j++] = cpu; - ret = copy_to_guest(op->u.get_para.affected_cpus, - affected_cpus, op->u.get_para.cpu_num); - xfree(affected_cpus); - if ( ret ) - return ret; - - if ( !(scaling_available_frequencies = - xzalloc_array(uint32_t, op->u.get_para.freq_num)) ) - return -ENOMEM; - for ( i = 0; i < op->u.get_para.freq_num; i++ ) - scaling_available_frequencies[i] = - pmpt->perf.states[i].core_frequency * 1000; - ret = copy_to_guest(op->u.get_para.scaling_available_frequencies, - scaling_available_frequencies, op->u.get_para.freq_num); - xfree(scaling_available_frequencies); - if ( ret ) - return ret; - - if ( !(scaling_available_governors = - xzalloc_array(char, gov_num * CPUFREQ_NAME_LEN)) ) - return -ENOMEM; - if ( (ret = read_scaling_available_governors(scaling_available_governors, - gov_num * CPUFREQ_NAME_LEN * sizeof(char))) ) - { - xfree(scaling_available_governors); - return ret; - } - ret = copy_to_guest(op->u.get_para.scaling_available_governors, - scaling_available_governors, gov_num * CPUFREQ_NAME_LEN); - xfree(scaling_available_governors); - if ( ret ) - return ret; - - op->u.get_para.cpuinfo_cur_freq = - cpufreq_driver->get ? cpufreq_driver->get(op->cpuid) : policy->cur; - op->u.get_para.cpuinfo_max_freq = policy->cpuinfo.max_freq; - op->u.get_para.cpuinfo_min_freq = policy->cpuinfo.min_freq; - op->u.get_para.scaling_cur_freq = policy->cur; - op->u.get_para.scaling_max_freq = policy->max; - op->u.get_para.scaling_min_freq = policy->min; - - if ( cpufreq_driver->name[0] ) - strlcpy(op->u.get_para.scaling_driver, - cpufreq_driver->name, CPUFREQ_NAME_LEN); - else - strlcpy(op->u.get_para.scaling_driver, "Unknown", CPUFREQ_NAME_LEN); - - if ( policy->governor->name[0] ) - strlcpy(op->u.get_para.scaling_governor, - policy->governor->name, CPUFREQ_NAME_LEN); - else - strlcpy(op->u.get_para.scaling_governor, "Unknown", CPUFREQ_NAME_LEN); - - /* governor specific para */ - if ( !strnicmp(op->u.get_para.scaling_governor, - "userspace", CPUFREQ_NAME_LEN) ) - { - op->u.get_para.u.userspace.scaling_setspeed = policy->cur; - } - - if ( !strnicmp(op->u.get_para.scaling_governor, - "ondemand", CPUFREQ_NAME_LEN) ) - { - ret = get_cpufreq_ondemand_para( - &op->u.get_para.u.ondemand.sampling_rate_max, - &op->u.get_para.u.ondemand.sampling_rate_min, - &op->u.get_para.u.ondemand.sampling_rate, - &op->u.get_para.u.ondemand.up_threshold); - } - op->u.get_para.turbo_enabled = cpufreq_get_turbo_status(op->cpuid); - - return ret; -} - -static int set_cpufreq_gov(struct xen_sysctl_pm_op *op) -{ - struct cpufreq_policy new_policy, *old_policy; - - old_policy = per_cpu(cpufreq_cpu_policy, op->cpuid); - if ( !old_policy ) - return -EINVAL; - - memcpy(&new_policy, old_policy, sizeof(struct cpufreq_policy)); - - new_policy.governor = __find_governor(op->u.set_gov.scaling_governor); - if (new_policy.governor == NULL) - return -EINVAL; - - return __cpufreq_set_policy(old_policy, &new_policy); -} - -static int set_cpufreq_para(struct xen_sysctl_pm_op *op) -{ - int ret = 0; - struct cpufreq_policy *policy; - - policy = per_cpu(cpufreq_cpu_policy, op->cpuid); - - if ( !policy || !policy->governor ) - return -EINVAL; - - switch(op->u.set_para.ctrl_type) - { - case SCALING_MAX_FREQ: - { - struct cpufreq_policy new_policy; - - memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); - new_policy.max = op->u.set_para.ctrl_value; - ret = __cpufreq_set_policy(policy, &new_policy); - - break; - } - - case SCALING_MIN_FREQ: - { - struct cpufreq_policy new_policy; - - memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); - new_policy.min = op->u.set_para.ctrl_value; - ret = __cpufreq_set_policy(policy, &new_policy); - - break; - } - - case SCALING_SETSPEED: - { - unsigned int freq =op->u.set_para.ctrl_value; - - if ( !strnicmp(policy->governor->name, - "userspace", CPUFREQ_NAME_LEN) ) - ret = write_userspace_scaling_setspeed(op->cpuid, freq); - else - ret = -EINVAL; - - break; - } - - case SAMPLING_RATE: - { - unsigned int sampling_rate = op->u.set_para.ctrl_value; - - if ( !strnicmp(policy->governor->name, - "ondemand", CPUFREQ_NAME_LEN) ) - ret = write_ondemand_sampling_rate(sampling_rate); - else - ret = -EINVAL; - - break; - } - - case UP_THRESHOLD: - { - unsigned int up_threshold = op->u.set_para.ctrl_value; - - if ( !strnicmp(policy->governor->name, - "ondemand", CPUFREQ_NAME_LEN) ) - ret = write_ondemand_up_threshold(up_threshold); - else - ret = -EINVAL; - - break; - } - - default: - ret = -EINVAL; - break; - } - - return ret; -} - -int do_pm_op(struct xen_sysctl_pm_op *op) -{ - int ret = 0; - const struct processor_pminfo *pmpt; - - if ( !op || op->cpuid >= nr_cpu_ids || !cpu_online(op->cpuid) ) - return -EINVAL; - pmpt = processor_pminfo[op->cpuid]; - - switch ( op->cmd & PM_PARA_CATEGORY_MASK ) - { - case CPUFREQ_PARA: - if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) ) - return -ENODEV; - if ( !pmpt || !(pmpt->perf.init & XEN_PX_INIT) ) - return -EINVAL; - break; - } - - switch ( op->cmd ) - { - case GET_CPUFREQ_PARA: - { - ret = get_cpufreq_para(op); - break; - } - - case SET_CPUFREQ_GOV: - { - ret = set_cpufreq_gov(op); - break; - } - - case SET_CPUFREQ_PARA: - { - ret = set_cpufreq_para(op); - break; - } - - case GET_CPUFREQ_AVGFREQ: - { - op->u.get_avgfreq = cpufreq_driver_getavg(op->cpuid, USR_GETAVG); - break; - } - - case XEN_SYSCTL_pm_op_set_sched_opt_smt: - { - uint32_t saved_value; - - saved_value = sched_smt_power_savings; - sched_smt_power_savings = !!op->u.set_sched_opt_smt; - op->u.set_sched_opt_smt = saved_value; - - break; - } - - case XEN_SYSCTL_pm_op_set_vcpu_migration_delay: - { - set_vcpu_migration_delay(op->u.set_vcpu_migration_delay); - break; - } - - case XEN_SYSCTL_pm_op_get_vcpu_migration_delay: - { - op->u.get_vcpu_migration_delay = get_vcpu_migration_delay(); - break; - } - - case XEN_SYSCTL_pm_op_get_max_cstate: - { - op->u.get_max_cstate = acpi_get_cstate_limit(); - break; - } - - case XEN_SYSCTL_pm_op_set_max_cstate: - { - acpi_set_cstate_limit(op->u.set_max_cstate); - break; - } - - case XEN_SYSCTL_pm_op_enable_turbo: - { - ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_ENABLED); - break; - } - - case XEN_SYSCTL_pm_op_disable_turbo: - { - ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_DISABLED); - break; - } - - default: - printk("not defined sub-hypercall @ do_pm_op\n"); - ret = -ENOSYS; - break; - } - - return ret; -} - -int acpi_set_pdc_bits(u32 acpi_id, XEN_GUEST_HANDLE_PARAM(uint32) pdc) -{ - u32 bits[3]; - int ret; - - if ( copy_from_guest(bits, pdc, 2) ) - ret = -EFAULT; - else if ( bits[0] != ACPI_PDC_REVISION_ID || !bits[1] ) - ret = -EINVAL; - else if ( copy_from_guest_offset(bits + 2, pdc, 2, 1) ) - ret = -EFAULT; - else - { - u32 mask = 0; - - if ( xen_processor_pmbits & XEN_PROCESSOR_PM_CX ) - mask |= ACPI_PDC_C_MASK | ACPI_PDC_SMP_C1PT; - if ( xen_processor_pmbits & XEN_PROCESSOR_PM_PX ) - mask |= ACPI_PDC_P_MASK | ACPI_PDC_SMP_C1PT; - if ( xen_processor_pmbits & XEN_PROCESSOR_PM_TX ) - mask |= ACPI_PDC_T_MASK | ACPI_PDC_SMP_C1PT; - bits[2] &= (ACPI_PDC_C_MASK | ACPI_PDC_P_MASK | ACPI_PDC_T_MASK | - ACPI_PDC_SMP_C1PT) & ~mask; - ret = arch_acpi_set_pdc_bits(acpi_id, bits, mask); - } - if ( !ret && __copy_to_guest_offset(pdc, 2, bits + 2, 1) ) - ret = -EFAULT; - - return ret; -} diff --git a/xen/drivers/pm/Kconfig b/xen/drivers/pm/Kconfig new file mode 100644 index 0000000..6d4fda1 --- /dev/null +++ b/xen/drivers/pm/Kconfig @@ -0,0 +1,3 @@ + +config HAS_PM + bool diff --git a/xen/drivers/pm/Makefile b/xen/drivers/pm/Makefile new file mode 100644 index 0000000..2073683 --- /dev/null +++ b/xen/drivers/pm/Makefile @@ -0,0 +1 @@ +obj-y += stat.o diff --git a/xen/drivers/pm/stat.c b/xen/drivers/pm/stat.c new file mode 100644 index 0000000..2dbde1c --- /dev/null +++ b/xen/drivers/pm/stat.c @@ -0,0 +1,526 @@ +/***************************************************************************** +# pmstat.c - Power Management statistic information (Px/Cx/Tx, etc.) +# +# Copyright (c) 2008, Liu Jinsong +# +# 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, see . +# +# The full GNU General Public License is included in this distribution in the +# file called LICENSE. +# +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +DEFINE_PER_CPU_READ_MOSTLY(struct pm_px *, cpufreq_statistic_data); + +/* + * Get PM statistic info + */ +int do_get_pm_info(struct xen_sysctl_get_pmstat *op) +{ + int ret = 0; + const struct processor_pminfo *pmpt; + + if ( !op || (op->cpuid >= nr_cpu_ids) || !cpu_online(op->cpuid) ) + return -EINVAL; + pmpt = processor_pminfo[op->cpuid]; + + switch ( op->type & PMSTAT_CATEGORY_MASK ) + { + case PMSTAT_CX: + if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_CX) ) + return -ENODEV; + break; + case PMSTAT_PX: + if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) ) + return -ENODEV; + if ( !cpufreq_driver ) + return -ENODEV; + if ( !pmpt || !(pmpt->perf.init & XEN_PX_INIT) ) + return -EINVAL; + break; + default: + return -ENODEV; + } + + switch ( op->type ) + { + case PMSTAT_get_max_px: + { + op->u.getpx.total = pmpt->perf.state_count; + break; + } + + case PMSTAT_get_pxstat: + { + uint32_t ct; + struct pm_px *pxpt; + spinlock_t *cpufreq_statistic_lock = + &per_cpu(cpufreq_statistic_lock, op->cpuid); + + spin_lock(cpufreq_statistic_lock); + + pxpt = per_cpu(cpufreq_statistic_data, op->cpuid); + if ( !pxpt || !pxpt->u.pt || !pxpt->u.trans_pt ) + { + spin_unlock(cpufreq_statistic_lock); + return -ENODATA; + } + + pxpt->u.usable = pmpt->perf.state_count - pmpt->perf.platform_limit; + + cpufreq_residency_update(op->cpuid, pxpt->u.cur); + + ct = pmpt->perf.state_count; + if ( copy_to_guest(op->u.getpx.trans_pt, pxpt->u.trans_pt, ct*ct) ) + { + spin_unlock(cpufreq_statistic_lock); + ret = -EFAULT; + break; + } + + if ( copy_to_guest(op->u.getpx.pt, pxpt->u.pt, ct) ) + { + spin_unlock(cpufreq_statistic_lock); + ret = -EFAULT; + break; + } + + op->u.getpx.total = pxpt->u.total; + op->u.getpx.usable = pxpt->u.usable; + op->u.getpx.last = pxpt->u.last; + op->u.getpx.cur = pxpt->u.cur; + + spin_unlock(cpufreq_statistic_lock); + + break; + } + + case PMSTAT_reset_pxstat: + { + cpufreq_statistic_reset(op->cpuid); + break; + } + + case PMSTAT_get_max_cx: + { + op->u.getcx.nr = pmstat_get_cx_nr(op->cpuid); + ret = 0; + break; + } + + case PMSTAT_get_cxstat: + { + ret = pmstat_get_cx_stat(op->cpuid, &op->u.getcx); + break; + } + + case PMSTAT_reset_cxstat: + { + ret = pmstat_reset_cx_stat(op->cpuid); + break; + } + + default: + printk("not defined sub-hypercall @ do_get_pm_info\n"); + ret = -ENOSYS; + break; + } + + return ret; +} + +/* + * 1. Get PM parameter + * 2. Provide user PM control + */ +static int read_scaling_available_governors(char *scaling_available_governors, + unsigned int size) +{ + unsigned int i = 0; + struct cpufreq_governor *t; + + if ( !scaling_available_governors ) + return -EINVAL; + + list_for_each_entry(t, &cpufreq_governor_list, governor_list) + { + i += scnprintf(&scaling_available_governors[i], + CPUFREQ_NAME_LEN, "%s ", t->name); + if ( i > size ) + return -EINVAL; + } + scaling_available_governors[i-1] = '\0'; + + return 0; +} + +static int get_cpufreq_para(struct xen_sysctl_pm_op *op) +{ + uint32_t ret = 0; + const struct processor_pminfo *pmpt; + struct cpufreq_policy *policy; + uint32_t gov_num = 0; + uint32_t *affected_cpus; + uint32_t *scaling_available_frequencies; + char *scaling_available_governors; + struct list_head *pos; + uint32_t cpu, i, j = 0; + + pmpt = processor_pminfo[op->cpuid]; + policy = per_cpu(cpufreq_cpu_policy, op->cpuid); + + if ( !pmpt || !pmpt->perf.states || + !policy || !policy->governor ) + return -EINVAL; + + list_for_each(pos, &cpufreq_governor_list) + gov_num++; + + if ( (op->u.get_para.cpu_num != cpumask_weight(policy->cpus)) || + (op->u.get_para.freq_num != pmpt->perf.state_count) || + (op->u.get_para.gov_num != gov_num) ) + { + op->u.get_para.cpu_num = cpumask_weight(policy->cpus); + op->u.get_para.freq_num = pmpt->perf.state_count; + op->u.get_para.gov_num = gov_num; + return -EAGAIN; + } + + if ( !(affected_cpus = xzalloc_array(uint32_t, op->u.get_para.cpu_num)) ) + return -ENOMEM; + for_each_cpu(cpu, policy->cpus) + affected_cpus[j++] = cpu; + ret = copy_to_guest(op->u.get_para.affected_cpus, + affected_cpus, op->u.get_para.cpu_num); + xfree(affected_cpus); + if ( ret ) + return ret; + + if ( !(scaling_available_frequencies = + xzalloc_array(uint32_t, op->u.get_para.freq_num)) ) + return -ENOMEM; + for ( i = 0; i < op->u.get_para.freq_num; i++ ) + scaling_available_frequencies[i] = + pmpt->perf.states[i].core_frequency * 1000; + ret = copy_to_guest(op->u.get_para.scaling_available_frequencies, + scaling_available_frequencies, op->u.get_para.freq_num); + xfree(scaling_available_frequencies); + if ( ret ) + return ret; + + if ( !(scaling_available_governors = + xzalloc_array(char, gov_num * CPUFREQ_NAME_LEN)) ) + return -ENOMEM; + if ( (ret = read_scaling_available_governors(scaling_available_governors, + gov_num * CPUFREQ_NAME_LEN * sizeof(char))) ) + { + xfree(scaling_available_governors); + return ret; + } + ret = copy_to_guest(op->u.get_para.scaling_available_governors, + scaling_available_governors, gov_num * CPUFREQ_NAME_LEN); + xfree(scaling_available_governors); + if ( ret ) + return ret; + + op->u.get_para.cpuinfo_cur_freq = + cpufreq_driver->get ? cpufreq_driver->get(op->cpuid) : policy->cur; + op->u.get_para.cpuinfo_max_freq = policy->cpuinfo.max_freq; + op->u.get_para.cpuinfo_min_freq = policy->cpuinfo.min_freq; + op->u.get_para.scaling_cur_freq = policy->cur; + op->u.get_para.scaling_max_freq = policy->max; + op->u.get_para.scaling_min_freq = policy->min; + + if ( cpufreq_driver->name[0] ) + strlcpy(op->u.get_para.scaling_driver, + cpufreq_driver->name, CPUFREQ_NAME_LEN); + else + strlcpy(op->u.get_para.scaling_driver, "Unknown", CPUFREQ_NAME_LEN); + + if ( policy->governor->name[0] ) + strlcpy(op->u.get_para.scaling_governor, + policy->governor->name, CPUFREQ_NAME_LEN); + else + strlcpy(op->u.get_para.scaling_governor, "Unknown", CPUFREQ_NAME_LEN); + + /* governor specific para */ + if ( !strnicmp(op->u.get_para.scaling_governor, + "userspace", CPUFREQ_NAME_LEN) ) + { + op->u.get_para.u.userspace.scaling_setspeed = policy->cur; + } + + if ( !strnicmp(op->u.get_para.scaling_governor, + "ondemand", CPUFREQ_NAME_LEN) ) + { + ret = get_cpufreq_ondemand_para( + &op->u.get_para.u.ondemand.sampling_rate_max, + &op->u.get_para.u.ondemand.sampling_rate_min, + &op->u.get_para.u.ondemand.sampling_rate, + &op->u.get_para.u.ondemand.up_threshold); + } + op->u.get_para.turbo_enabled = cpufreq_get_turbo_status(op->cpuid); + + return ret; +} + +static int set_cpufreq_gov(struct xen_sysctl_pm_op *op) +{ + struct cpufreq_policy new_policy, *old_policy; + + old_policy = per_cpu(cpufreq_cpu_policy, op->cpuid); + if ( !old_policy ) + return -EINVAL; + + memcpy(&new_policy, old_policy, sizeof(struct cpufreq_policy)); + + new_policy.governor = __find_governor(op->u.set_gov.scaling_governor); + if (new_policy.governor == NULL) + return -EINVAL; + + return __cpufreq_set_policy(old_policy, &new_policy); +} + +static int set_cpufreq_para(struct xen_sysctl_pm_op *op) +{ + int ret = 0; + struct cpufreq_policy *policy; + + policy = per_cpu(cpufreq_cpu_policy, op->cpuid); + + if ( !policy || !policy->governor ) + return -EINVAL; + + switch(op->u.set_para.ctrl_type) + { + case SCALING_MAX_FREQ: + { + struct cpufreq_policy new_policy; + + memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); + new_policy.max = op->u.set_para.ctrl_value; + ret = __cpufreq_set_policy(policy, &new_policy); + + break; + } + + case SCALING_MIN_FREQ: + { + struct cpufreq_policy new_policy; + + memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); + new_policy.min = op->u.set_para.ctrl_value; + ret = __cpufreq_set_policy(policy, &new_policy); + + break; + } + + case SCALING_SETSPEED: + { + unsigned int freq =op->u.set_para.ctrl_value; + + if ( !strnicmp(policy->governor->name, + "userspace", CPUFREQ_NAME_LEN) ) + ret = write_userspace_scaling_setspeed(op->cpuid, freq); + else + ret = -EINVAL; + + break; + } + + case SAMPLING_RATE: + { + unsigned int sampling_rate = op->u.set_para.ctrl_value; + + if ( !strnicmp(policy->governor->name, + "ondemand", CPUFREQ_NAME_LEN) ) + ret = write_ondemand_sampling_rate(sampling_rate); + else + ret = -EINVAL; + + break; + } + + case UP_THRESHOLD: + { + unsigned int up_threshold = op->u.set_para.ctrl_value; + + if ( !strnicmp(policy->governor->name, + "ondemand", CPUFREQ_NAME_LEN) ) + ret = write_ondemand_up_threshold(up_threshold); + else + ret = -EINVAL; + + break; + } + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int do_pm_op(struct xen_sysctl_pm_op *op) +{ + int ret = 0; + const struct processor_pminfo *pmpt; + + if ( !op || op->cpuid >= nr_cpu_ids || !cpu_online(op->cpuid) ) + return -EINVAL; + pmpt = processor_pminfo[op->cpuid]; + + switch ( op->cmd & PM_PARA_CATEGORY_MASK ) + { + case CPUFREQ_PARA: + if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) ) + return -ENODEV; + if ( !pmpt || !(pmpt->perf.init & XEN_PX_INIT) ) + return -EINVAL; + break; + } + + switch ( op->cmd ) + { + case GET_CPUFREQ_PARA: + { + ret = get_cpufreq_para(op); + break; + } + + case SET_CPUFREQ_GOV: + { + ret = set_cpufreq_gov(op); + break; + } + + case SET_CPUFREQ_PARA: + { + ret = set_cpufreq_para(op); + break; + } + + case GET_CPUFREQ_AVGFREQ: + { + op->u.get_avgfreq = cpufreq_driver_getavg(op->cpuid, USR_GETAVG); + break; + } + + case XEN_SYSCTL_pm_op_set_sched_opt_smt: + { + uint32_t saved_value; + + saved_value = sched_smt_power_savings; + sched_smt_power_savings = !!op->u.set_sched_opt_smt; + op->u.set_sched_opt_smt = saved_value; + + break; + } + + case XEN_SYSCTL_pm_op_set_vcpu_migration_delay: + { + set_vcpu_migration_delay(op->u.set_vcpu_migration_delay); + break; + } + + case XEN_SYSCTL_pm_op_get_vcpu_migration_delay: + { + op->u.get_vcpu_migration_delay = get_vcpu_migration_delay(); + break; + } + + case XEN_SYSCTL_pm_op_get_max_cstate: + { + op->u.get_max_cstate = acpi_get_cstate_limit(); + break; + } + + case XEN_SYSCTL_pm_op_set_max_cstate: + { + acpi_set_cstate_limit(op->u.set_max_cstate); + break; + } + + case XEN_SYSCTL_pm_op_enable_turbo: + { + ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_ENABLED); + break; + } + + case XEN_SYSCTL_pm_op_disable_turbo: + { + ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_DISABLED); + break; + } + + default: + printk("not defined sub-hypercall @ do_pm_op\n"); + ret = -ENOSYS; + break; + } + + return ret; +} + +int acpi_set_pdc_bits(u32 acpi_id, XEN_GUEST_HANDLE_PARAM(uint32) pdc) +{ + u32 bits[3]; + int ret; + + if ( copy_from_guest(bits, pdc, 2) ) + ret = -EFAULT; + else if ( bits[0] != ACPI_PDC_REVISION_ID || !bits[1] ) + ret = -EINVAL; + else if ( copy_from_guest_offset(bits + 2, pdc, 2, 1) ) + ret = -EFAULT; + else + { + u32 mask = 0; + + if ( xen_processor_pmbits & XEN_PROCESSOR_PM_CX ) + mask |= ACPI_PDC_C_MASK | ACPI_PDC_SMP_C1PT; + if ( xen_processor_pmbits & XEN_PROCESSOR_PM_PX ) + mask |= ACPI_PDC_P_MASK | ACPI_PDC_SMP_C1PT; + if ( xen_processor_pmbits & XEN_PROCESSOR_PM_TX ) + mask |= ACPI_PDC_T_MASK | ACPI_PDC_SMP_C1PT; + bits[2] &= (ACPI_PDC_C_MASK | ACPI_PDC_P_MASK | ACPI_PDC_T_MASK | + ACPI_PDC_SMP_C1PT) & ~mask; + ret = arch_acpi_set_pdc_bits(acpi_id, bits, mask); + } + if ( !ret && __copy_to_guest_offset(pdc, 2, bits + 2, 1) ) + ret = -EFAULT; + + return ret; +}