Files
xen/tools/libxc/xc_pm.c
T
Keir Fraser 5cc436c1d2 libxc: eliminate static variables, use xentoollog; API change
This patch eliminate the global variables in libxenctrl (used for
logging and error reporting).

Instead the information which was in the global variables is now in a
new xc_interface* opaque structure, which xc_interface open returns
instead of the raw file descriptor; furthermore, logging is done via
xentoollog.

There are three new parameters to xc_interface_open to control the
logging, but existing callers can just pass "0" for all three to get
the old behaviour.

All libxc callers have been adjusted accordingly.

Also update QEMU_TAG for corresponding qemu change.

Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
2010-05-28 09:30:19 +01:00

434 lines
13 KiB
C

/******************************************************************************
* xc_pm.c - Libxc API for Xen Power Management (Px/Cx/Tx, etc.) statistic
*
* Copyright (c) 2008, Liu Jinsong <jinsong.liu@intel.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include <errno.h>
#include <stdbool.h>
#include "xc_private.h"
/*
* Get PM statistic info
*/
int xc_pm_get_max_px(xc_interface *xch, int cpuid, int *max_px)
{
DECLARE_SYSCTL;
int ret;
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_get_max_px;
sysctl.u.get_pmstat.cpuid = cpuid;
ret = xc_sysctl(xch, &sysctl);
if ( ret )
return ret;
*max_px = sysctl.u.get_pmstat.u.getpx.total;
return ret;
}
int xc_pm_get_pxstat(xc_interface *xch, int cpuid, struct xc_px_stat *pxpt)
{
DECLARE_SYSCTL;
int max_px, ret;
if ( !pxpt || !(pxpt->trans_pt) || !(pxpt->pt) )
return -EINVAL;
if ( (ret = xc_pm_get_max_px(xch, cpuid, &max_px)) != 0)
return ret;
if ( (ret = lock_pages(pxpt->trans_pt,
max_px * max_px * sizeof(uint64_t))) != 0 )
return ret;
if ( (ret = lock_pages(pxpt->pt,
max_px * sizeof(struct xc_px_val))) != 0 )
{
unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t));
return ret;
}
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_get_pxstat;
sysctl.u.get_pmstat.cpuid = cpuid;
sysctl.u.get_pmstat.u.getpx.total = max_px;
set_xen_guest_handle(sysctl.u.get_pmstat.u.getpx.trans_pt, pxpt->trans_pt);
set_xen_guest_handle(sysctl.u.get_pmstat.u.getpx.pt,
(pm_px_val_t *)pxpt->pt);
ret = xc_sysctl(xch, &sysctl);
if ( ret )
{
unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t));
unlock_pages(pxpt->pt, max_px * sizeof(struct xc_px_val));
return ret;
}
pxpt->total = sysctl.u.get_pmstat.u.getpx.total;
pxpt->usable = sysctl.u.get_pmstat.u.getpx.usable;
pxpt->last = sysctl.u.get_pmstat.u.getpx.last;
pxpt->cur = sysctl.u.get_pmstat.u.getpx.cur;
unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t));
unlock_pages(pxpt->pt, max_px * sizeof(struct xc_px_val));
return ret;
}
int xc_pm_reset_pxstat(xc_interface *xch, int cpuid)
{
DECLARE_SYSCTL;
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_reset_pxstat;
sysctl.u.get_pmstat.cpuid = cpuid;
return xc_sysctl(xch, &sysctl);
}
int xc_pm_get_max_cx(xc_interface *xch, int cpuid, int *max_cx)
{
DECLARE_SYSCTL;
int ret = 0;
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_get_max_cx;
sysctl.u.get_pmstat.cpuid = cpuid;
if ( (ret = xc_sysctl(xch, &sysctl)) != 0 )
return ret;
*max_cx = sysctl.u.get_pmstat.u.getcx.nr;
return ret;
}
int xc_pm_get_cxstat(xc_interface *xch, int cpuid, struct xc_cx_stat *cxpt)
{
DECLARE_SYSCTL;
int max_cx, ret;
if( !cxpt || !(cxpt->triggers) || !(cxpt->residencies) )
return -EINVAL;
if ( (ret = xc_pm_get_max_cx(xch, cpuid, &max_cx)) )
goto unlock_0;
if ( (ret = lock_pages(cxpt, sizeof(struct xc_cx_stat))) )
goto unlock_0;
if ( (ret = lock_pages(cxpt->triggers, max_cx * sizeof(uint64_t))) )
goto unlock_1;
if ( (ret = lock_pages(cxpt->residencies, max_cx * sizeof(uint64_t))) )
goto unlock_2;
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_get_cxstat;
sysctl.u.get_pmstat.cpuid = cpuid;
set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.triggers, cxpt->triggers);
set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.residencies,
cxpt->residencies);
if ( (ret = xc_sysctl(xch, &sysctl)) )
goto unlock_3;
cxpt->nr = sysctl.u.get_pmstat.u.getcx.nr;
cxpt->last = sysctl.u.get_pmstat.u.getcx.last;
cxpt->idle_time = sysctl.u.get_pmstat.u.getcx.idle_time;
unlock_3:
unlock_pages(cxpt->residencies, max_cx * sizeof(uint64_t));
unlock_2:
unlock_pages(cxpt->triggers, max_cx * sizeof(uint64_t));
unlock_1:
unlock_pages(cxpt, sizeof(struct xc_cx_stat));
unlock_0:
return ret;
}
int xc_pm_reset_cxstat(xc_interface *xch, int cpuid)
{
DECLARE_SYSCTL;
sysctl.cmd = XEN_SYSCTL_get_pmstat;
sysctl.u.get_pmstat.type = PMSTAT_reset_cxstat;
sysctl.u.get_pmstat.cpuid = cpuid;
return xc_sysctl(xch, &sysctl);
}
/*
* 1. Get PM parameter
* 2. Provide user PM control
*/
int xc_get_cpufreq_para(xc_interface *xch, int cpuid,
struct xc_get_cpufreq_para *user_para)
{
DECLARE_SYSCTL;
int ret = 0;
struct xen_get_cpufreq_para *sys_para = &sysctl.u.pm_op.u.get_para;
bool has_num = user_para->cpu_num &&
user_para->freq_num &&
user_para->gov_num;
if ( (xch < 0) || !user_para )
return -EINVAL;
if ( has_num )
{
if ( (!user_para->affected_cpus) ||
(!user_para->scaling_available_frequencies) ||
(!user_para->scaling_available_governors) )
return -EINVAL;
if ( (ret = lock_pages(user_para->affected_cpus,
user_para->cpu_num * sizeof(uint32_t))) )
goto unlock_1;
if ( (ret = lock_pages(user_para->scaling_available_frequencies,
user_para->freq_num * sizeof(uint32_t))) )
goto unlock_2;
if ( (ret = lock_pages(user_para->scaling_available_governors,
user_para->gov_num * CPUFREQ_NAME_LEN * sizeof(char))) )
goto unlock_3;
set_xen_guest_handle(sys_para->affected_cpus,
user_para->affected_cpus);
set_xen_guest_handle(sys_para->scaling_available_frequencies,
user_para->scaling_available_frequencies);
set_xen_guest_handle(sys_para->scaling_available_governors,
user_para->scaling_available_governors);
}
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = GET_CPUFREQ_PARA;
sysctl.u.pm_op.cpuid = cpuid;
sys_para->cpu_num = user_para->cpu_num;
sys_para->freq_num = user_para->freq_num;
sys_para->gov_num = user_para->gov_num;
ret = xc_sysctl(xch, &sysctl);
if ( ret )
{
if ( errno == EAGAIN )
{
user_para->cpu_num = sys_para->cpu_num;
user_para->freq_num = sys_para->freq_num;
user_para->gov_num = sys_para->gov_num;
ret = -errno;
}
if ( has_num )
goto unlock_4;
goto unlock_1;
}
else
{
user_para->cpuinfo_cur_freq = sys_para->cpuinfo_cur_freq;
user_para->cpuinfo_max_freq = sys_para->cpuinfo_max_freq;
user_para->cpuinfo_min_freq = sys_para->cpuinfo_min_freq;
user_para->scaling_cur_freq = sys_para->scaling_cur_freq;
user_para->scaling_max_freq = sys_para->scaling_max_freq;
user_para->scaling_min_freq = sys_para->scaling_min_freq;
user_para->turbo_enabled = sys_para->turbo_enabled;
memcpy(user_para->scaling_driver,
sys_para->scaling_driver, CPUFREQ_NAME_LEN);
memcpy(user_para->scaling_governor,
sys_para->scaling_governor, CPUFREQ_NAME_LEN);
/* copy to user_para no matter what cpufreq governor */
XC_BUILD_BUG_ON(sizeof(((struct xc_get_cpufreq_para *)0)->u) !=
sizeof(((struct xen_get_cpufreq_para *)0)->u));
memcpy(&user_para->u, &sys_para->u, sizeof(sys_para->u));
}
unlock_4:
unlock_pages(user_para->scaling_available_governors,
user_para->gov_num * CPUFREQ_NAME_LEN * sizeof(char));
unlock_3:
unlock_pages(user_para->scaling_available_frequencies,
user_para->freq_num * sizeof(uint32_t));
unlock_2:
unlock_pages(user_para->affected_cpus,
user_para->cpu_num * sizeof(uint32_t));
unlock_1:
return ret;
}
int xc_set_cpufreq_gov(xc_interface *xch, int cpuid, char *govname)
{
DECLARE_SYSCTL;
char *scaling_governor = sysctl.u.pm_op.u.set_gov.scaling_governor;
if ( (xch < 0) || (!govname) )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = SET_CPUFREQ_GOV;
sysctl.u.pm_op.cpuid = cpuid;
strncpy(scaling_governor, govname, CPUFREQ_NAME_LEN);
scaling_governor[CPUFREQ_NAME_LEN - 1] = '\0';
return xc_sysctl(xch, &sysctl);
}
int xc_set_cpufreq_para(xc_interface *xch, int cpuid,
int ctrl_type, int ctrl_value)
{
DECLARE_SYSCTL;
if ( xch < 0 )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = SET_CPUFREQ_PARA;
sysctl.u.pm_op.cpuid = cpuid;
sysctl.u.pm_op.u.set_para.ctrl_type = ctrl_type;
sysctl.u.pm_op.u.set_para.ctrl_value = ctrl_value;
return xc_sysctl(xch, &sysctl);
}
int xc_get_cpufreq_avgfreq(xc_interface *xch, int cpuid, int *avg_freq)
{
int ret = 0;
DECLARE_SYSCTL;
if ( (xch < 0) || (!avg_freq) )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = GET_CPUFREQ_AVGFREQ;
sysctl.u.pm_op.cpuid = cpuid;
ret = xc_sysctl(xch, &sysctl);
*avg_freq = sysctl.u.pm_op.u.get_avgfreq;
return ret;
}
/* value: 0 - disable sched_smt_power_savings
1 - enable sched_smt_power_savings
*/
int xc_set_sched_opt_smt(xc_interface *xch, uint32_t value)
{
int rc;
DECLARE_SYSCTL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_sched_opt_smt;
sysctl.u.pm_op.cpuid = 0;
sysctl.u.pm_op.u.set_sched_opt_smt = value;
rc = do_sysctl(xch, &sysctl);
return rc;
}
int xc_set_vcpu_migration_delay(xc_interface *xch, uint32_t value)
{
int rc;
DECLARE_SYSCTL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_vcpu_migration_delay;
sysctl.u.pm_op.cpuid = 0;
sysctl.u.pm_op.u.set_vcpu_migration_delay = value;
rc = do_sysctl(xch, &sysctl);
return rc;
}
int xc_get_vcpu_migration_delay(xc_interface *xch, uint32_t *value)
{
int rc;
DECLARE_SYSCTL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_get_vcpu_migration_delay;
sysctl.u.pm_op.cpuid = 0;
rc = do_sysctl(xch, &sysctl);
if (!rc && value)
*value = sysctl.u.pm_op.u.get_vcpu_migration_delay;
return rc;
}
int xc_get_cpuidle_max_cstate(xc_interface *xch, uint32_t *value)
{
int rc;
DECLARE_SYSCTL;
if ( xch < 0 || !value )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_get_max_cstate;
sysctl.u.pm_op.cpuid = 0;
sysctl.u.pm_op.u.get_max_cstate = 0;
rc = do_sysctl(xch, &sysctl);
*value = sysctl.u.pm_op.u.get_max_cstate;
return rc;
}
int xc_set_cpuidle_max_cstate(xc_interface *xch, uint32_t value)
{
DECLARE_SYSCTL;
if ( xch < 0 )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_max_cstate;
sysctl.u.pm_op.cpuid = 0;
sysctl.u.pm_op.u.set_max_cstate = value;
return do_sysctl(xch, &sysctl);
}
int xc_enable_turbo(xc_interface *xch, int cpuid)
{
DECLARE_SYSCTL;
if ( xch < 0 )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_enable_turbo;
sysctl.u.pm_op.cpuid = cpuid;
return do_sysctl(xch, &sysctl);
}
int xc_disable_turbo(xc_interface *xch, int cpuid)
{
DECLARE_SYSCTL;
if ( xch < 0 )
return -EINVAL;
sysctl.cmd = XEN_SYSCTL_pm_op;
sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_disable_turbo;
sysctl.u.pm_op.cpuid = cpuid;
return do_sysctl(xch, &sysctl);
}