Provide Px statistic data to user through libxc
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 16 May 2008 08:37:19 +0000 (09:37 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 16 May 2008 08:37:19 +0000 (09:37 +0100)
Sampling and collecting dynamic Px statistic data, including
Px state value, Px count, Px residency time, Px transition
matrix, etc. Provide Px statistic data to user through libxc.

Signed-off-by: Liu Jinsong <jinsong.liu@intel.com>
tools/libxc/Makefile
tools/libxc/xc_pm.c [new file with mode: 0644]
tools/libxc/xenctrl.h
xen/arch/x86/acpi/Makefile
xen/arch/x86/acpi/cpufreq/cpufreq.c
xen/arch/x86/acpi/cpufreq/utility.c
xen/arch/x86/acpi/pmstat.c [new file with mode: 0644]
xen/arch/x86/platform_hypercall.c
xen/common/sysctl.c
xen/include/acpi/cpufreq/processor_perf.h
xen/include/public/sysctl.h

index 3fd926ad476e51b7a427ad7d95bfa92597aa73c8..47bfb2c00451ccc46284e4f9ade7aeb2336bb32d 100644 (file)
@@ -20,6 +20,7 @@ CTRL_SRCS-y       += xc_private.c
 CTRL_SRCS-y       += xc_sedf.c
 CTRL_SRCS-y       += xc_csched.c
 CTRL_SRCS-y       += xc_tbuf.c
+CTRL_SRCS-y       += xc_pm.c
 ifneq ($(stubdom),y)
 CTRL_SRCS-y       += xc_resume.c
 endif
diff --git a/tools/libxc/xc_pm.c b/tools/libxc/xc_pm.c
new file mode 100644 (file)
index 0000000..0378cbd
--- /dev/null
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * 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 "xc_private.h"
+
+int xc_pm_get_max_px(int xc_handle, 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(xc_handle, &sysctl);
+    if ( ret )
+        return ret;
+
+    *max_px = sysctl.u.get_pmstat.u.getpx.total;
+    return ret;
+}
+
+int xc_pm_get_pxstat(int xc_handle, 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(xc_handle, 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;
+    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(xc_handle, &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(int xc_handle, 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(xc_handle, &sysctl);
+}
index 40e44b1fbfbde132d24154140b0e8a5f75450183..130a05e19d04ccbcb32bee0b3ca6d0be605af85f 100644 (file)
@@ -1034,4 +1034,23 @@ void xc_cpuid_to_str(const unsigned int *regs,
                      char **strs);
 #endif
 
+struct xc_px_val {
+    uint64_t freq;        /* Px core frequency */
+    uint64_t residency;   /* Px residency time */
+    uint64_t count;       /* Px transition count */
+};
+
+struct xc_px_stat {
+    uint8_t total;        /* total Px states */
+    uint8_t usable;       /* usable Px states */
+    uint8_t last;         /* last Px state */
+    uint8_t cur;          /* current Px state */
+    uint64_t *trans_pt;   /* Px transition table */
+    struct xc_px_val *pt;
+};
+
+int xc_pm_get_max_px(int xc_handle, int cpuid, int *max_px);
+int xc_pm_get_pxstat(int xc_handle, int cpuid, struct xc_px_stat *pxpt);
+int xc_pm_reset_pxstat(int xc_handle, int cpuid);
+
 #endif /* XENCTRL_H */
index 1a82f5d75c36e38d847a0eb331182d9f75842ebf..e4cb83b0c6cbe2ab45a828e5599e08d7a311fcce 100644 (file)
@@ -2,3 +2,4 @@ subdir-y += cpufreq
 
 obj-y += boot.o
 obj-y += power.o suspend.o wakeup_prot.o cpu_idle.o
+obj-y += pmstat.o
index 2ac4be4e5505c4c8c435dbddc94278c90a69473a..53b7f0cd457e11a3d94d404128e304daa9e2cc7c 100644 (file)
@@ -369,6 +369,8 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
     if (!check_freqs(cmd.mask, freqs.new, data))
         return -EAGAIN;
 
+    px_statistic_update(cmd.mask, perf->state, next_perf_state);
+
     perf->state = next_perf_state;
     policy->cur = freqs.new;
 
@@ -581,9 +583,13 @@ int acpi_cpufreq_init(void)
     for_each_online_cpu(i) {
         xen_px_policy[i].cpu = i;
 
+        ret = px_statistic_init(i);
+        if (ret)
+            goto out;
+
         ret = acpi_cpufreq_cpu_init(&xen_px_policy[i]);
         if (ret)
-            goto cpufreq_init_out;
+            goto out;
     }
 
     /* setup ondemand cpufreq */
@@ -593,10 +599,10 @@ int acpi_cpufreq_init(void)
         i = first_cpu(pt[dom]);
         ret = cpufreq_governor_dbs(&xen_px_policy[i], CPUFREQ_GOV_START);
         if (ret)
-            goto cpufreq_init_out;
+            goto out;
     }
 
-cpufreq_init_out:
+out:
     xfree(pt);
    
     return ret;
index 83475935dc724126e17318ebeba63bb083638494..89934b320b08aa8c44c40126dc4674eea947ee87 100644 (file)
 
 struct cpufreq_driver *cpufreq_driver;
 
+/*********************************************************************
+ *                    Px STATISTIC INFO                              *
+ *********************************************************************/
+
+void px_statistic_update(cpumask_t cpumask, uint8_t from, uint8_t to)
+{
+    uint32_t i;
+    uint64_t now;
+
+    now = NOW();
+
+    for_each_cpu_mask(i, cpumask) {
+        struct pm_px *pxpt = &px_statistic_data[i];
+        uint32_t statnum = processor_pminfo[i].perf.state_count;
+
+        pxpt->u.last = from;
+        pxpt->u.cur = to;
+        pxpt->u.pt[to].count++;
+        pxpt->u.pt[from].residency += now - pxpt->prev_state_wall;
+
+        (*(pxpt->u.trans_pt + from*statnum + to))++;
+
+        pxpt->prev_state_wall = now;
+    }
+}
+
+int px_statistic_init(int cpuid)
+{
+    uint32_t i, count;
+    struct pm_px *pxpt = &px_statistic_data[cpuid];
+    struct processor_pminfo *pmpt = &processor_pminfo[cpuid];
+
+    count = pmpt->perf.state_count;
+
+    pxpt->u.trans_pt = xmalloc_array(uint64_t, count * count);
+    if (!pxpt->u.trans_pt)
+        return -ENOMEM;
+
+    pxpt->u.pt = xmalloc_array(struct pm_px_val, count);
+    if (!pxpt->u.pt) {
+        xfree(pxpt->u.trans_pt);
+        return -ENOMEM;
+    }
+
+    memset(pxpt->u.trans_pt, 0, count * count * (sizeof(uint64_t)));
+    memset(pxpt->u.pt, 0, count * (sizeof(struct pm_px_val)));
+
+    pxpt->u.total = pmpt->perf.state_count;
+    pxpt->u.usable = pmpt->perf.state_count - pmpt->perf.ppc;
+
+    for (i=0; i < pmpt->perf.state_count; i++)
+        pxpt->u.pt[i].freq = pmpt->perf.states[i].core_frequency;
+
+    pxpt->prev_state_wall = NOW();
+
+    return 0;
+}
+
+void px_statistic_reset(int cpuid)
+{
+    uint32_t i, j, count;
+    struct pm_px *pxpt = &px_statistic_data[cpuid];
+
+    count = processor_pminfo[cpuid].perf.state_count;
+
+    for (i=0; i < count; i++) {
+        pxpt->u.pt[i].residency = 0;
+        pxpt->u.pt[i].count = 0;
+
+        for (j=0; j < count; j++)
+            *(pxpt->u.trans_pt + i*count + j) = 0;
+    }
+
+    pxpt->prev_state_wall = NOW();
+}
+
+
 /*********************************************************************
  *                   FREQUENCY TABLE HELPERS                         *
  *********************************************************************/
diff --git a/xen/arch/x86/acpi/pmstat.c b/xen/arch/x86/acpi/pmstat.c
new file mode 100644 (file)
index 0000000..a18a78e
--- /dev/null
@@ -0,0 +1,110 @@
+/*****************************************************************************
+#  pmstat.c - Power Management statistic information (Px/Cx/Tx, etc.)
+#
+#  Copyright (c) 2008, Liu Jinsong <jinsong.liu@intel.com>
+#
+# 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.
+#
+# The full GNU General Public License is included in this distribution in the
+# file called LICENSE.
+#
+*****************************************************************************/
+
+#include <xen/config.h>
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/irq.h>
+#include <xen/iocap.h>
+#include <xen/compat.h>
+#include <xen/guest_access.h>
+#include <asm/current.h>
+#include <public/xen.h>
+#include <xen/cpumask.h>
+#include <asm/processor.h>
+#include <xen/percpu.h>
+
+#include <public/sysctl.h>
+#include <acpi/cpufreq/cpufreq.h>
+
+struct pm_px px_statistic_data[NR_CPUS];
+
+int do_get_pm_info(struct xen_sysctl_get_pmstat *op)
+{
+    int ret = 0;
+    struct pm_px *pxpt = &px_statistic_data[op->cpuid];
+    struct processor_pminfo *pmpt = &processor_pminfo[op->cpuid];
+
+    /* to protect the case when Px was controlled by dom0-kernel */
+    /* or when CPU_FREQ not set in which case ACPI Px objects not parsed */
+    if ( !pmpt->perf.init )
+        return -EINVAL;
+
+    if ( !cpu_online(op->cpuid) )
+        return -EINVAL;
+
+    switch( op->type )
+    {
+    case PMSTAT_get_max_px:
+    {
+        op->u.getpx.total = pmpt->perf.state_count;
+        break;
+    }
+
+    case PMSTAT_get_pxstat:
+    {
+        uint64_t now, ct;
+
+        now = NOW();
+        pxpt->u.usable = pmpt->perf.state_count - pmpt->perf.ppc;
+        pxpt->u.pt[pxpt->u.cur].residency += now - pxpt->prev_state_wall;
+        pxpt->prev_state_wall = now;
+
+        ct = pmpt->perf.state_count;
+        if ( copy_to_guest(op->u.getpx.trans_pt, pxpt->u.trans_pt, ct*ct) )
+        {
+            ret = -EFAULT;
+            break;
+        }
+
+        if ( copy_to_guest(op->u.getpx.pt, pxpt->u.pt, ct) )
+        {
+            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;
+
+        break;
+    }
+
+    case PMSTAT_reset_pxstat:
+    {
+        px_statistic_reset(op->cpuid);
+        break;
+    }
+
+    default:
+        printk("not defined sub-hypercall @ do_get_pm_info\n");
+        ret = -ENOSYS;
+        break;
+    }
+
+    return ret;
+}
index 5538eb3149aade8a6220188fbce447f37ed2a919..dbcc4a2e5bc9f3f8b85937b1fbcd4e9431ca612f 100644 (file)
@@ -403,7 +403,10 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
 
             if ( xenpxpt->flags == ( XEN_PX_PCT | XEN_PX_PSS | 
                 XEN_PX_PSD | XEN_PX_PPC ) )
+            {
+                pxpt->init =1;
                 cpu_count++;
+            }
             if ( cpu_count == num_online_cpus() )
                 ret = acpi_cpufreq_init();
             break;
index 1b9c6a58a8cf3bff468d395835711d68eec40cdb..0b32378df780211015f6effc76037ca45e1696d7 100644 (file)
@@ -25,6 +25,8 @@
 #include <xen/nodemask.h>
 #include <xsm/xsm.h>
 
+extern int do_get_pm_info(struct xen_sysctl_get_pmstat *op);
+
 extern long arch_do_sysctl(
     struct xen_sysctl *op, XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl);
 
@@ -196,6 +198,20 @@ long do_sysctl(XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl)
     }
     break;
 
+    case XEN_SYSCTL_get_pmstat:
+    {
+        ret = do_get_pm_info(&op->u.get_pmstat);
+        if ( ret )
+            break;
+
+        if ( copy_to_guest(u_sysctl, op, 1) )
+        {
+            ret = -EFAULT;
+            break;
+        }
+    }
+    break;
+
     default:
         ret = arch_do_sysctl(op, u_sysctl);
         break;
index 27add2a77fb2928a04386e67971f4d3bc42baee4..3596947910b08f41c616272b5d331857613c8498 100644 (file)
@@ -2,9 +2,13 @@
 #define __XEN_PROCESSOR_PM_H__
 
 #include <public/platform.h>
+#include <public/sysctl.h>
 
 int get_cpu_id(u8);
 int acpi_cpufreq_init(void);
+void px_statistic_update(cpumask_t, uint8_t, uint8_t);
+int  px_statistic_init(int);
+void px_statistic_reset(int);
 
 struct processor_performance {
     uint32_t state;
@@ -16,15 +20,32 @@ struct processor_performance {
     struct xen_psd_package domain_info;
     cpumask_t shared_cpu_map;
     uint32_t shared_type;
+
+    uint32_t init;
 };
 
 struct processor_pminfo {
     uint32_t acpi_id;
     uint32_t id;
-    uint32_t flag;
     struct processor_performance    perf;
 };
 
 extern struct processor_pminfo processor_pminfo[NR_CPUS];
 
+struct px_stat {
+    uint8_t total;        /* total Px states */
+    uint8_t usable;       /* usable Px states */
+    uint8_t last;         /* last Px state */
+    uint8_t cur;          /* current Px state */
+    uint64_t *trans_pt;   /* Px transition table */
+    pm_px_val_t *pt;
+};
+
+struct pm_px {
+    struct px_stat u;
+    uint64_t prev_state_wall;
+};
+
+extern struct pm_px px_statistic_data[NR_CPUS];
+
 #endif /* __XEN_PROCESSOR_PM_H__ */
index b66c02190c9426737b6a6cae19f5e153aa040c50..0a2c55b7cf7b7739244e7a6c94742992aa661a8c 100644 (file)
@@ -212,7 +212,41 @@ struct xen_sysctl_availheap {
 };
 typedef struct xen_sysctl_availheap xen_sysctl_availheap_t;
 DEFINE_XEN_GUEST_HANDLE(xen_sysctl_availheap_t);
+
+#define XEN_SYSCTL_get_pmstat        10
+struct pm_px_val {
+    uint64_aligned_t freq;        /* Px core frequency */
+    uint64_aligned_t residency;   /* Px residency time */
+    uint64_aligned_t count;       /* Px transition count */
+};
+typedef struct pm_px_val pm_px_val_t;
+DEFINE_XEN_GUEST_HANDLE(pm_px_val_t);
+
+struct pm_px_stat {
+    uint8_t total;        /* total Px states */
+    uint8_t usable;       /* usable Px states */
+    uint8_t last;         /* last Px state */
+    uint8_t cur;          /* current Px state */
+    XEN_GUEST_HANDLE_64(uint64) trans_pt;   /* Px transition table */
+    XEN_GUEST_HANDLE_64(pm_px_val_t) pt;
+};
+typedef struct pm_px_stat pm_px_stat_t;
+DEFINE_XEN_GUEST_HANDLE(pm_px_stat_t);
+
+struct xen_sysctl_get_pmstat {
+#define PMSTAT_get_max_px   0x11
+#define PMSTAT_get_pxstat   0x12
+#define PMSTAT_reset_pxstat 0x13
+    uint32_t type;
+    uint32_t cpuid;
+    union {
+        struct pm_px_stat getpx;
+        /* other struct for cx, tx, etc */
+    } u;
+};
+typedef struct xen_sysctl_get_pmstat xen_sysctl_get_pmstat_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_get_pmstat_t);
+
 struct xen_sysctl {
     uint32_t cmd;
     uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
@@ -226,6 +260,7 @@ struct xen_sysctl {
         struct xen_sysctl_debug_keys        debug_keys;
         struct xen_sysctl_getcpuinfo        getcpuinfo;
         struct xen_sysctl_availheap         availheap;
+        struct xen_sysctl_get_pmstat        get_pmstat;
         uint8_t                             pad[128];
     } u;
 };