Yu, Ke
2009-Jul-19 06:46 UTC
[Xen-devel] [PATCH][pvops_dom0][2/4] Introduce the external control operation interface for domain0 ACPI parser
Introduce the external control operation interface for domain0 ACPI parser From: Yu Ke <ke.yu@intel.com> This patch introduces the interface of external control operation, and adds hooks to the acpi sub-system, including the acpi_processor_driver, and the related library functions. === Overview == Requirement: Xen hypervisor need Cx/Px ACPI info to do the Cx/Px states power management. This info is provided by BIOS ACPI table. Since hypervisor has no ACPI parser, this info has to be parsed by domain0 kernel ACPI sub-system, and then passed to hypervisor by hypercall. To make this happen, the key point is to add hook in the kernel ACPI sub-system. Fortunately, kernel already has good abstraction, and only several places need to add hook. To be more detail, there is an acpi_processor_driver (in drivers/acpi/processor_core.c) , which all the Cx/Px parsing event will go to. This driver will call its acpi processor event handler, e.g. add/remove, start/stop, notify to handle these events. These event handlers in turn will call some library functions (in drivers/acpi/processor_perflib.c), e.g. acpi_processor_ppc_has_changed, acpi_processor_ppc_has_changed, acpi_processor_cst_has_changed, to finish the acpi info parsing. So the conclusion is: adding hooks in acpi_processor_driver and those related library functions will satisfy our requirement. To make the added hook cleaner, we introduce an interface called external control operation (struct processor_extcntl_ops). All the hooks are encapsulated in this interface processor_extcntl_ops . Here the "external" means the acpi processor is controlled by external entity, e.g. VMM. Every kind of external entity can has its implementation of this interface. In this patch, the interface for Xen is implemented. Signed-off-by: Yu Ke <ke.yu@intel.com> Signed-off-by: Tian Kevin <kevin.tian@intel.com> --- drivers/acpi/Kconfig | 5 + drivers/acpi/Makefile | 1 drivers/acpi/processor_core.c | 16 +++ drivers/acpi/processor_extcntl.c | 208 ++++++++++++++++++++++++++++++++++++++ drivers/acpi/processor_idle.c | 24 ++++ drivers/acpi/processor_perflib.c | 9 +- include/acpi/processor.h | 81 +++++++++++++++ 7 files changed, 338 insertions(+), 6 deletions(-) create mode 100644 drivers/acpi/processor_extcntl.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 431f8b4..e932ee6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -332,4 +332,9 @@ config ACPI_SBS To compile this driver as a module, choose M here: the modules will be called sbs and sbshc. +config PROCESSOR_EXTERNAL_CONTROL + bool + depends on ACPI_PROCESSOR && CPU_FREQ + default y + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 03a985b..2a42a08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_ACPI_SBS) += sbs.o processor-y := processor_core.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o processor-$(CONFIG_CPU_FREQ) += processor_perflib.o +processor-$(CONFIG_PROCESSOR_EXTERNAL_CONTROL) += processor_extcntl.o diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 45ad328..0b6facc 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -740,6 +740,10 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) acpi_processor_power_init(pr, device); + result = processor_extcntl_prepare(pr); + if (result) + goto end; + pr->cdev = thermal_cooling_device_register("Processor", device, &processor_cooling_ops); if (IS_ERR(pr->cdev)) { @@ -952,6 +956,10 @@ int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) if (!pr) return -ENODEV; + if (processor_cntl_external()) + processor_notify_external(pr, + PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); + if ((pr->id >= 0) && (pr->id < nr_cpu_ids)) { kobject_uevent(&(*device)->dev.kobj, KOBJ_ONLINE); } @@ -991,11 +999,19 @@ static void __ref acpi_processor_hotplug_notify(acpi_handle handle, break; } + if (processor_cntl_external()) + processor_notify_external(pr, + PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); + if (pr->id >= 0 && (pr->id < nr_cpu_ids)) { kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); break; } + if (processor_cntl_external()) + processor_notify_external(pr, PROCESSOR_HOTPLUG, + HOTPLUG_TYPE_REMOVE); + result = acpi_processor_start(device); if ((!result) && ((pr->id >= 0) && (pr->id < nr_cpu_ids))) { kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); diff --git a/drivers/acpi/processor_extcntl.c b/drivers/acpi/processor_extcntl.c new file mode 100644 index 0000000..af3191f --- /dev/null +++ b/drivers/acpi/processor_extcntl.c @@ -0,0 +1,208 @@ +/* + * processor_extcntl.c - channel to external control logic + * + * Copyright (C) 2008, Intel corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/acpi.h> +#include <linux/pm.h> +#include <linux/cpu.h> + +#include <acpi/processor.h> + +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("acpi_processor") + +static int processor_extcntl_get_performance(struct acpi_processor *pr); +/* + * External processor control logic may register with its own set of + * ops to get ACPI related notification. One example is like VMM. + */ +const struct processor_extcntl_ops *processor_extcntl_ops; +EXPORT_SYMBOL(processor_extcntl_ops); + +static int processor_notify_smm(void) +{ + acpi_status status; + static int is_done = 0; + + /* only need successfully notify BIOS once */ + /* avoid double notification which may lead to unexpected result */ + if (is_done) + return 0; + + /* Can''t write pstate_cnt to smi_cmd if either value is zero */ + if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"No SMI port or pstate_cnt\n")); + return 0; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n", + acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command)); + + status = acpi_os_write_port(acpi_gbl_FADT.smi_command, + (u32) acpi_gbl_FADT.pstate_control, 8); + if (ACPI_FAILURE(status)) + return status; + + is_done = 1; + + return 0; +} + +int processor_notify_external(struct acpi_processor *pr, int event, int type) +{ + int ret = -EINVAL; + + if (!processor_cntl_external()) + return -EINVAL; + + switch (event) { + case PROCESSOR_PM_INIT: + case PROCESSOR_PM_CHANGE: + if ((type >= PM_TYPE_MAX) || + !processor_extcntl_ops->pm_ops[type]) + break; + + ret = processor_extcntl_ops->pm_ops[type](pr, event); + break; + case PROCESSOR_HOTPLUG: + if (processor_extcntl_ops->hotplug) + ret = processor_extcntl_ops->hotplug(pr, type); + break; + default: + printk(KERN_ERR "Unsupport processor events %d.\n", event); + break; + } + + return ret; +} + +/* + * External control logic can decide to grab full or part of physical + * processor control bits. Take a VMM for example, physical processors + * are owned by VMM and thus existence information like hotplug is + * always required to be notified to VMM. Similar is processor idle + * state which is also necessarily controlled by VMM. But for other + * control bits like performance/throttle states, VMM may choose to + * control or not upon its own policy. + */ +void processor_extcntl_register(struct processor_extcntl_ops* ops) +{ + if (!processor_extcntl_ops) + processor_extcntl_ops=ops; +} +EXPORT_SYMBOL(processor_extcntl_register); + +/* + * This is called from ACPI processor init, and targeted to hold + * some tricky housekeeping jobs to satisfy external control model. + * For example, we may put dependency parse stub here for idle + * and performance state. Those information may be not available + * if splitting from dom0 control logic like cpufreq driver. + */ +int processor_extcntl_prepare(struct acpi_processor *pr) +{ + + /* Initialize performance states */ + if (processor_pmperf_external()) + processor_extcntl_get_performance(pr); + + return 0; +} + +/* + * Existing ACPI module does parse performance states at some point, + * when acpi-cpufreq driver is loaded which however is something + * we''d like to disable to avoid confliction with external control + * logic. So we have to collect raw performance information here + * when ACPI processor object is found and started. + */ +static int processor_extcntl_get_performance(struct acpi_processor *pr) +{ + int ret; + struct acpi_processor_performance *perf; + struct acpi_psd_package *pdomain; + + if (pr->performance) + return -EBUSY; + + perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); + if (!perf) + return -ENOMEM; + + pr->performance = perf; + /* Get basic performance state information */ + ret = acpi_processor_get_performance_info(pr); + if (ret < 0) + goto err_out; + + /* + * Well, here we need retrieve performance dependency information + * from _PSD object. The reason why existing interface is not used + * is due to the reason that existing interface sticks to Linux cpu + * id to construct some bitmap, however we want to split ACPI + * processor objects from Linux cpu id logic. For example, even + * when Linux is configured as UP, we still want to parse all ACPI + * processor objects to external logic. In this case, it''s preferred + * to use ACPI ID instead. + */ + pdomain = &pr->performance->domain_info; + pdomain->num_processors = 0; + ret = acpi_processor_get_psd(pr); + if (ret < 0) { + /* + * _PSD is optional - assume no coordination if absent (or + * broken), matching native kernels'' behavior. + */ + pdomain->num_entries = ACPI_PSD_REV0_ENTRIES; + pdomain->revision = ACPI_PSD_REV0_REVISION; + pdomain->domain = pr->acpi_id; + pdomain->coord_type = DOMAIN_COORD_TYPE_SW_ALL; + pdomain->num_processors = 1; + } + + /* Some sanity check */ + if ((pdomain->revision != ACPI_PSD_REV0_REVISION) || + (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) || + ((pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL) && + (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY) && + (pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL))) { + ret = -EINVAL; + goto err_out; + } + + /* Last step is to notify BIOS that external logic exists */ + processor_notify_smm(); + + processor_notify_external(pr, PROCESSOR_PM_INIT, PM_TYPE_PERF); + + return 0; +err_out: + pr->performance = NULL; + kfree(perf); + return ret; +} diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index abbe2bb..49ccb84 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -425,6 +425,12 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) cx.power = obj->integer.value; +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL + /* cache control methods to notify external logic */ + if (processor_pm_external()) + memcpy(&cx.reg, reg, sizeof(*reg)); +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ + current_count++; memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); @@ -1120,6 +1126,13 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) if (!pr->flags.power_setup_done) return -ENODEV; + if (processor_pm_external()) { + acpi_processor_get_power_info(pr); + processor_notify_external(pr, + PROCESSOR_PM_CHANGE, PM_TYPE_IDLE); + return ret; + } + cpuidle_pause_and_lock(); cpuidle_disable_device(&pr->power.dev); acpi_processor_get_power_info(pr); @@ -1183,9 +1196,14 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, * platforms that only support C1. */ if (pr->flags.power) { - acpi_processor_setup_cpuidle(pr); - if (cpuidle_register_device(&pr->power.dev)) - return -EIO; + if (processor_pm_external()) + processor_notify_external(pr, + PROCESSOR_PM_INIT, PM_TYPE_IDLE); + else { + acpi_processor_setup_cpuidle(pr); + if (cpuidle_register_device(&pr->power.dev)) + return -EIO; + } printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); for (i = 1; i <= pr->power.count; i++) diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index cafb410..b222cdb 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -154,13 +154,16 @@ int acpi_processor_ppc_has_changed(struct acpi_processor *pr) { int ret; - if (ignore_ppc) + if (ignore_ppc && !processor_pmperf_external()) return 0; ret = acpi_processor_get_platform_limit(pr); if (ret < 0) return (ret); + else if (processor_pmperf_external()) + return processor_notify_external(pr, + PROCESSOR_PM_CHANGE, PM_TYPE_PERF); else return cpufreq_update_policy(pr->id); } @@ -324,7 +327,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) return result; } -static int acpi_processor_get_performance_info(struct acpi_processor *pr) +int acpi_processor_get_performance_info(struct acpi_processor *pr) { int result = 0; acpi_status status = AE_OK; @@ -426,7 +429,7 @@ int acpi_processor_notify_smm(struct module *calling_module) EXPORT_SYMBOL(acpi_processor_notify_smm); -static int acpi_processor_get_psd(struct acpi_processor *pr) +int acpi_processor_get_psd(struct acpi_processor *pr) { int result = 0; acpi_status status = AE_OK; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index b09c4fd..d6bb2d2 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -77,6 +77,10 @@ struct acpi_processor_cx { struct acpi_processor_cx_policy promotion; struct acpi_processor_cx_policy demotion; char desc[ACPI_CX_DESC_LEN]; +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL + /* Require raw information for external control logic */ + struct acpi_power_register reg; +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ }; struct acpi_processor_power { @@ -295,6 +299,8 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx void acpi_processor_ppc_init(void); void acpi_processor_ppc_exit(void); int acpi_processor_ppc_has_changed(struct acpi_processor *pr); +int acpi_processor_get_performance_info(struct acpi_processor *pr); +int acpi_processor_get_psd(struct acpi_processor *pr); #else static inline void acpi_processor_ppc_init(void) { @@ -352,4 +358,79 @@ static inline void acpi_thermal_cpufreq_exit(void) } #endif +/* + * Following are interfaces geared to external processor PM control + * logic like a VMM + */ +/* Events notified to external control logic */ +#define PROCESSOR_PM_INIT 1 +#define PROCESSOR_PM_CHANGE 2 +#define PROCESSOR_HOTPLUG 3 + +/* Objects for the PM events */ +#define PM_TYPE_IDLE 0 +#define PM_TYPE_PERF 1 +#define PM_TYPE_THR 2 +#define PM_TYPE_MAX 3 + +/* Processor hotplug events */ +#define HOTPLUG_TYPE_ADD 0 +#define HOTPLUG_TYPE_REMOVE 1 + +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL +struct processor_extcntl_ops { + /* Transfer processor PM events to external control logic */ +int (*pm_ops[PM_TYPE_MAX])(struct acpi_processor *pr, int event); + /* Notify physical processor status to external control logic */ + int (*hotplug)(struct acpi_processor *pr, int type); +}; +extern const struct processor_extcntl_ops *processor_extcntl_ops; + +static inline int processor_cntl_external(void) +{ + return (processor_extcntl_ops != NULL); +} + +static inline int processor_pm_external(void) +{ + return processor_cntl_external() && + (processor_extcntl_ops->pm_ops[PM_TYPE_IDLE] != NULL); +} + +static inline int processor_pmperf_external(void) +{ + return processor_cntl_external() && + (processor_extcntl_ops->pm_ops[PM_TYPE_PERF] != NULL); +} + +static inline int processor_pmthr_external(void) +{ + return processor_cntl_external() && + (processor_extcntl_ops->pm_ops[PM_TYPE_THR] != NULL); +} + +extern int processor_notify_external(struct acpi_processor *pr, + int event, int type); +extern void processor_extcntl_register(struct processor_extcntl_ops* ops); +extern int processor_extcntl_prepare(struct acpi_processor *pr); +#else +static inline int processor_cntl_external(void) {return 0;} +static inline int processor_pm_external(void) {return 0;} +static inline int processor_pmperf_external(void) {return 0;} +static inline int processor_pmthr_external(void) {return 0;} +static inline int processor_notify_external(struct acpi_processor *pr, + int event, int type) +{ + return 0; +} +static inline void processor_extcntl_register(struct processor_extcntl_ops* ops) +{ + return 0; +} +static inline int processor_extcntl_prepare(struct acpi_processor *pr) +{ + return 0; +} +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ + #endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jeremy Fitzhardinge
2009-Jul-20 20:32 UTC
[Xen-devel] Re: [PATCH][pvops_dom0][2/4] Introduce the external control operation interface for domain0 ACPI parser
On 07/18/09 23:46, Yu, Ke wrote:> Introduce the external control operation interface for domain0 ACPI parser > > From: Yu Ke <ke.yu@intel.com> > > This patch introduces the interface of external control operation, and > adds hooks to the acpi sub-system, including the acpi_processor_driver, > and the related library functions. > > === Overview ==> > Requirement: Xen hypervisor need Cx/Px ACPI info to do the Cx/Px states > power management. This info is provided by BIOS ACPI table. Since > hypervisor has no ACPI parser, this info has to be parsed by domain0 > kernel ACPI sub-system, and then passed to hypervisor by hypercall. >Xen does parse some ACPI tables; the main thing its missing is an AML interpreter. Do the power state info tables require AML, or are they otherwise very complex to parse? How much code are we talking about?> To make this happen, the key point is to add hook in the kernel ACPI > sub-system. Fortunately, kernel already has good abstraction, and > only several places need to add hook. To be more detail, there is an > acpi_processor_driver (in drivers/acpi/processor_core.c) , which all the > Cx/Px parsing event will go to. This driver will call its acpi processor > event handler, e.g. add/remove, start/stop, notify to handle these > events. These event handlers in turn will call some library functions (in > drivers/acpi/processor_perflib.c), e.g. acpi_processor_ppc_has_changed, > acpi_processor_ppc_has_changed, acpi_processor_cst_has_changed, to finish > the acpi info parsing. > > So the conclusion is: adding hooks in acpi_processor_driver and those > related library functions will satisfy our requirement. > > To make the added hook cleaner, we introduce an interface called > external control operation (struct processor_extcntl_ops). All the hooks > are encapsulated in this interface processor_extcntl_ops . Here the > "external" means the acpi processor is controlled by external entity, > e.g. VMM. Every kind of external entity can has its implementation of > this interface. In this patch, the interface for Xen is implemented. > > Signed-off-by: Yu Ke <ke.yu@intel.com> > Signed-off-by: Tian Kevin <kevin.tian@intel.com> > --- > > drivers/acpi/Kconfig | 5 + > drivers/acpi/Makefile | 1 > drivers/acpi/processor_core.c | 16 +++ > drivers/acpi/processor_extcntl.c | 208 ++++++++++++++++++++++++++++++++++++++ > drivers/acpi/processor_idle.c | 24 ++++ > drivers/acpi/processor_perflib.c | 9 +- > include/acpi/processor.h | 81 +++++++++++++++ > 7 files changed, 338 insertions(+), 6 deletions(-) > create mode 100644 drivers/acpi/processor_extcntl.c > > > diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig > index 431f8b4..e932ee6 100644 > --- a/drivers/acpi/Kconfig > +++ b/drivers/acpi/Kconfig > @@ -332,4 +332,9 @@ config ACPI_SBS > To compile this driver as a module, choose M here: > the modules will be called sbs and sbshc. > > +config PROCESSOR_EXTERNAL_CONTROL > + bool > + depends on ACPI_PROCESSOR && CPU_FREQ > + default y > + > endif # ACPI > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index 03a985b..2a42a08 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -61,3 +61,4 @@ obj-$(CONFIG_ACPI_SBS) += sbs.o > processor-y := processor_core.o processor_throttling.o > processor-y += processor_idle.o processor_thermal.o > processor-$(CONFIG_CPU_FREQ) += processor_perflib.o > +processor-$(CONFIG_PROCESSOR_EXTERNAL_CONTROL) += processor_extcntl.o > diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c > index 45ad328..0b6facc 100644 > --- a/drivers/acpi/processor_core.c > +++ b/drivers/acpi/processor_core.c > @@ -740,6 +740,10 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) > > acpi_processor_power_init(pr, device); > > + result = processor_extcntl_prepare(pr); > + if (result) > + goto end; > + > pr->cdev = thermal_cooling_device_register("Processor", device, > &processor_cooling_ops); > if (IS_ERR(pr->cdev)) { > @@ -952,6 +956,10 @@ int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) > if (!pr) > return -ENODEV; > > + if (processor_cntl_external()) > + processor_notify_external(pr, > + PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); > + > if ((pr->id >= 0) && (pr->id < nr_cpu_ids)) { > kobject_uevent(&(*device)->dev.kobj, KOBJ_ONLINE); > } > @@ -991,11 +999,19 @@ static void __ref acpi_processor_hotplug_notify(acpi_handle handle, > break; > } > > + if (processor_cntl_external()) > + processor_notify_external(pr, > + PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); > + > if (pr->id >= 0 && (pr->id < nr_cpu_ids)) { > kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); > break; > } > > + if (processor_cntl_external()) > + processor_notify_external(pr, PROCESSOR_HOTPLUG, > + HOTPLUG_TYPE_REMOVE); > + > result = acpi_processor_start(device); > if ((!result) && ((pr->id >= 0) && (pr->id < nr_cpu_ids))) { > kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); > diff --git a/drivers/acpi/processor_extcntl.c b/drivers/acpi/processor_extcntl.c > new file mode 100644 > index 0000000..af3191f > --- /dev/null > +++ b/drivers/acpi/processor_extcntl.c > @@ -0,0 +1,208 @@ > +/* > + * processor_extcntl.c - channel to external control logic > + * > + * Copyright (C) 2008, Intel corporation > + * > + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + * > + * 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 <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/types.h> > +#include <linux/acpi.h> > +#include <linux/pm.h> > +#include <linux/cpu.h> > + > +#include <acpi/processor.h> > + > +#define ACPI_PROCESSOR_CLASS "processor" > +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" > +#define _COMPONENT ACPI_PROCESSOR_COMPONENT > +ACPI_MODULE_NAME("acpi_processor") > + > +static int processor_extcntl_get_performance(struct acpi_processor *pr); > +/* > + * External processor control logic may register with its own set of > + * ops to get ACPI related notification. One example is like VMM. > + */ > +const struct processor_extcntl_ops *processor_extcntl_ops; > +EXPORT_SYMBOL(processor_extcntl_ops); > + > +static int processor_notify_smm(void) > +{ > + acpi_status status; > + static int is_done = 0; > + > + /* only need successfully notify BIOS once */ > + /* avoid double notification which may lead to unexpected result */ > + if (is_done) > + return 0; > + > + /* Can''t write pstate_cnt to smi_cmd if either value is zero */ > + if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) { > + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"No SMI port or pstate_cnt\n")); > + return 0; > + } > + > + ACPI_DEBUG_PRINT((ACPI_DB_INFO, > + "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n", > + acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command)); > + > + status = acpi_os_write_port(acpi_gbl_FADT.smi_command, > + (u32) acpi_gbl_FADT.pstate_control, 8); > + if (ACPI_FAILURE(status)) > + return status; > + > + is_done = 1; > + > + return 0; > +} > + > +int processor_notify_external(struct acpi_processor *pr, int event, int type) > +{ > + int ret = -EINVAL; > + > + if (!processor_cntl_external()) > + return -EINVAL; > + > + switch (event) { > + case PROCESSOR_PM_INIT: > + case PROCESSOR_PM_CHANGE: > + if ((type >= PM_TYPE_MAX) || > + !processor_extcntl_ops->pm_ops[type]) > + break; > + > + ret = processor_extcntl_ops->pm_ops[type](pr, event); > + break; > + case PROCESSOR_HOTPLUG: > + if (processor_extcntl_ops->hotplug) > + ret = processor_extcntl_ops->hotplug(pr, type); > + break; > + default: > + printk(KERN_ERR "Unsupport processor events %d.\n", event); > + break; > + } > + > + return ret; > +} > + > +/* > + * External control logic can decide to grab full or part of physical > + * processor control bits. Take a VMM for example, physical processors > + * are owned by VMM and thus existence information like hotplug is > + * always required to be notified to VMM. Similar is processor idle > + * state which is also necessarily controlled by VMM. But for other > + * control bits like performance/throttle states, VMM may choose to > + * control or not upon its own policy. > + */ > +void processor_extcntl_register(struct processor_extcntl_ops* ops) > +{ > + if (!processor_extcntl_ops) > + processor_extcntl_ops=ops; > +} > +EXPORT_SYMBOL(processor_extcntl_register); > + > +/* > + * This is called from ACPI processor init, and targeted to hold > + * some tricky housekeeping jobs to satisfy external control model. > + * For example, we may put dependency parse stub here for idle > + * and performance state. Those information may be not available > + * if splitting from dom0 control logic like cpufreq driver. > + */ > +int processor_extcntl_prepare(struct acpi_processor *pr) > +{ > + > + /* Initialize performance states */ > + if (processor_pmperf_external()) > + processor_extcntl_get_performance(pr); > + > + return 0; > +} > + > +/* > + * Existing ACPI module does parse performance states at some point, > + * when acpi-cpufreq driver is loaded which however is something > + * we''d like to disable to avoid confliction with external control > + * logic. So we have to collect raw performance information here > + * when ACPI processor object is found and started. > + */ > +static int processor_extcntl_get_performance(struct acpi_processor *pr) > +{ > + int ret; > + struct acpi_processor_performance *perf; > + struct acpi_psd_package *pdomain; > + > + if (pr->performance) > + return -EBUSY; > + > + perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); > + if (!perf) > + return -ENOMEM; > + > + pr->performance = perf; > + /* Get basic performance state information */ > + ret = acpi_processor_get_performance_info(pr); > + if (ret < 0) > + goto err_out; > + > + /* > + * Well, here we need retrieve performance dependency information > + * from _PSD object. The reason why existing interface is not used > + * is due to the reason that existing interface sticks to Linux cpu > + * id to construct some bitmap, however we want to split ACPI > + * processor objects from Linux cpu id logic. For example, even > + * when Linux is configured as UP, we still want to parse all ACPI > + * processor objects to external logic. In this case, it''s preferred > + * to use ACPI ID instead. > + */ > + pdomain = &pr->performance->domain_info; > + pdomain->num_processors = 0; > + ret = acpi_processor_get_psd(pr); > + if (ret < 0) { > + /* > + * _PSD is optional - assume no coordination if absent (or > + * broken), matching native kernels'' behavior. > + */ > + pdomain->num_entries = ACPI_PSD_REV0_ENTRIES; > + pdomain->revision = ACPI_PSD_REV0_REVISION; > + pdomain->domain = pr->acpi_id; > + pdomain->coord_type = DOMAIN_COORD_TYPE_SW_ALL; > + pdomain->num_processors = 1; > + } > + > + /* Some sanity check */ > + if ((pdomain->revision != ACPI_PSD_REV0_REVISION) || > + (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) || > + ((pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL) && > + (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY) && > + (pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL))) { > + ret = -EINVAL; > + goto err_out; > + } > + > + /* Last step is to notify BIOS that external logic exists */ > + processor_notify_smm(); > + > + processor_notify_external(pr, PROCESSOR_PM_INIT, PM_TYPE_PERF); > + > + return 0; > +err_out: > + pr->performance = NULL; > + kfree(perf); > + return ret; > +} > diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c > index abbe2bb..49ccb84 100644 > --- a/drivers/acpi/processor_idle.c > +++ b/drivers/acpi/processor_idle.c > @@ -425,6 +425,12 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) > > cx.power = obj->integer.value; > > +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL > + /* cache control methods to notify external logic */ > + if (processor_pm_external()) > + memcpy(&cx.reg, reg, sizeof(*reg)); > +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ >This #ifdef should be unnecessary.> + > current_count++; > memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); > > @@ -1120,6 +1126,13 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) > if (!pr->flags.power_setup_done) > return -ENODEV; > > + if (processor_pm_external()) { > + acpi_processor_get_power_info(pr); > + processor_notify_external(pr, > + PROCESSOR_PM_CHANGE, PM_TYPE_IDLE); > + return ret; > + } > + > cpuidle_pause_and_lock(); > cpuidle_disable_device(&pr->power.dev); > acpi_processor_get_power_info(pr); > @@ -1183,9 +1196,14 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, > * platforms that only support C1. > */ > if (pr->flags.power) { > - acpi_processor_setup_cpuidle(pr); > - if (cpuidle_register_device(&pr->power.dev)) > - return -EIO; > + if (processor_pm_external()) > + processor_notify_external(pr, > + PROCESSOR_PM_INIT, PM_TYPE_IDLE); > + else { > + acpi_processor_setup_cpuidle(pr); > + if (cpuidle_register_device(&pr->power.dev)) > + return -EIO; > + } > > printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); > for (i = 1; i <= pr->power.count; i++) > diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c > index cafb410..b222cdb 100644 > --- a/drivers/acpi/processor_perflib.c > +++ b/drivers/acpi/processor_perflib.c > @@ -154,13 +154,16 @@ int acpi_processor_ppc_has_changed(struct acpi_processor *pr) > { > int ret; > > - if (ignore_ppc) > + if (ignore_ppc && !processor_pmperf_external()) > return 0; > > ret = acpi_processor_get_platform_limit(pr); > > if (ret < 0) > return (ret); > + else if (processor_pmperf_external()) > + return processor_notify_external(pr, > + PROCESSOR_PM_CHANGE, PM_TYPE_PERF); > else > return cpufreq_update_policy(pr->id); > } > @@ -324,7 +327,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) > return result; > } > > -static int acpi_processor_get_performance_info(struct acpi_processor *pr) > +int acpi_processor_get_performance_info(struct acpi_processor *pr) > { > int result = 0; > acpi_status status = AE_OK; > @@ -426,7 +429,7 @@ int acpi_processor_notify_smm(struct module *calling_module) > > EXPORT_SYMBOL(acpi_processor_notify_smm); > > -static int acpi_processor_get_psd(struct acpi_processor *pr) > +int acpi_processor_get_psd(struct acpi_processor *pr) > { > int result = 0; > acpi_status status = AE_OK; > diff --git a/include/acpi/processor.h b/include/acpi/processor.h > index b09c4fd..d6bb2d2 100644 > --- a/include/acpi/processor.h > +++ b/include/acpi/processor.h > @@ -77,6 +77,10 @@ struct acpi_processor_cx { > struct acpi_processor_cx_policy promotion; > struct acpi_processor_cx_policy demotion; > char desc[ACPI_CX_DESC_LEN]; > +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL > + /* Require raw information for external control logic */ > + struct acpi_power_register reg; > +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ > }; > > struct acpi_processor_power { > @@ -295,6 +299,8 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx > void acpi_processor_ppc_init(void); > void acpi_processor_ppc_exit(void); > int acpi_processor_ppc_has_changed(struct acpi_processor *pr); > +int acpi_processor_get_performance_info(struct acpi_processor *pr); > +int acpi_processor_get_psd(struct acpi_processor *pr); > #else > static inline void acpi_processor_ppc_init(void) > { > @@ -352,4 +358,79 @@ static inline void acpi_thermal_cpufreq_exit(void) > } > #endif > > +/* > + * Following are interfaces geared to external processor PM control > + * logic like a VMM > + */ > +/* Events notified to external control logic */ > +#define PROCESSOR_PM_INIT 1 > +#define PROCESSOR_PM_CHANGE 2 > +#define PROCESSOR_HOTPLUG 3 > + > +/* Objects for the PM events */ > +#define PM_TYPE_IDLE 0 > +#define PM_TYPE_PERF 1 > +#define PM_TYPE_THR 2 > +#define PM_TYPE_MAX 3 > + > +/* Processor hotplug events */ > +#define HOTPLUG_TYPE_ADD 0 > +#define HOTPLUG_TYPE_REMOVE 1 > + > +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL > +struct processor_extcntl_ops { > + /* Transfer processor PM events to external control logic */ > +int (*pm_ops[PM_TYPE_MAX])(struct acpi_processor *pr, int event); > + /* Notify physical processor status to external control logic */ > + int (*hotplug)(struct acpi_processor *pr, int type); > +}; > +extern const struct processor_extcntl_ops *processor_extcntl_ops; > + > +static inline int processor_cntl_external(void) > +{ > + return (processor_extcntl_ops != NULL); > +} > + > +static inline int processor_pm_external(void) > +{ > + return processor_cntl_external() && > + (processor_extcntl_ops->pm_ops[PM_TYPE_IDLE] != NULL); > +} > + > +static inline int processor_pmperf_external(void) > +{ > + return processor_cntl_external() && > + (processor_extcntl_ops->pm_ops[PM_TYPE_PERF] != NULL); > +} > + > +static inline int processor_pmthr_external(void) > +{ > + return processor_cntl_external() && > + (processor_extcntl_ops->pm_ops[PM_TYPE_THR] != NULL); > +} > + > +extern int processor_notify_external(struct acpi_processor *pr, > + int event, int type); > +extern void processor_extcntl_register(struct processor_extcntl_ops* ops); > +extern int processor_extcntl_prepare(struct acpi_processor *pr); > +#else > +static inline int processor_cntl_external(void) {return 0;} > +static inline int processor_pm_external(void) {return 0;} > +static inline int processor_pmperf_external(void) {return 0;} > +static inline int processor_pmthr_external(void) {return 0;} > +static inline int processor_notify_external(struct acpi_processor *pr, > + int event, int type) > +{ > + return 0; > +} > +static inline void processor_extcntl_register(struct processor_extcntl_ops* ops) > +{ > + return 0; > +} > +static inline int processor_extcntl_prepare(struct acpi_processor *pr) > +{ > + return 0; > +} > +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ > + > #endif > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Yu, Ke
2009-Jul-21 03:02 UTC
[Xen-devel] RE: [PATCH][pvops_dom0][2/4] Introduce the external control operation interface for domain0 ACPI parser
>From: Jeremy Fitzhardinge [mailto:jeremy@goop.org] >Sent: Tuesday, July 21, 2009 4:32 AM >To: Yu, Ke >Cc: xen-devel@lists.xensource.com; Tian, Kevin >Subject: Re: [PATCH][pvops_dom0][2/4] Introduce the external control >operation interface for domain0 ACPI parser > >On 07/18/09 23:46, Yu, Ke wrote: >> diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c >> index abbe2bb..49ccb84 100644 >> --- a/drivers/acpi/processor_idle.c >> +++ b/drivers/acpi/processor_idle.c >> @@ -425,6 +425,12 @@ static int >acpi_processor_get_power_info_cst(struct acpi_processor *pr) >> >> cx.power = obj->integer.value; >> >> +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL >> + /* cache control methods to notify external logic */ >> + if (processor_pm_external()) >> + memcpy(&cx.reg, reg, sizeof(*reg)); >> +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ >> > >This #ifdef should be unnecessary.This "#ifdef" is the counterpart of the following patch. The cx.reg definition is embraced by CONFIG_PROCESSOR_EXTERNAL_CONTROL, so the code manipulating on the cx.reg also need "#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL"> diff --git a/include/acpi/processor.h b/include/acpi/processor.h > index b09c4fd..d6bb2d2 100644 > --- a/include/acpi/processor.h > +++ b/include/acpi/processor.h > @@ -77,6 +77,10 @@ struct acpi_processor_cx { > struct acpi_processor_cx_policy promotion; > struct acpi_processor_cx_policy demotion; > char desc[ACPI_CX_DESC_LEN]; > +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL > + /* Require raw information for external control logic */ > + struct acpi_power_register reg; > +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ > };Best Regards Ke _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jeremy Fitzhardinge
2009-Jul-28 17:38 UTC
[Xen-devel] Re: [PATCH][pvops_dom0][2/4] Introduce the external control operation interface for domain0 ACPI parser
On 07/20/09 20:02, Yu, Ke wrote:>> From: Jeremy Fitzhardinge [mailto:jeremy@goop.org] >> Sent: Tuesday, July 21, 2009 4:32 AM >> To: Yu, Ke >> Cc: xen-devel@lists.xensource.com; Tian, Kevin >> Subject: Re: [PATCH][pvops_dom0][2/4] Introduce the external control >> operation interface for domain0 ACPI parser >> >> On 07/18/09 23:46, Yu, Ke wrote: >> >>> diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c >>> index abbe2bb..49ccb84 100644 >>> --- a/drivers/acpi/processor_idle.c >>> +++ b/drivers/acpi/processor_idle.c >>> @@ -425,6 +425,12 @@ static int >>> >> acpi_processor_get_power_info_cst(struct acpi_processor *pr) >> >>> cx.power = obj->integer.value; >>> >>> +#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL >>> + /* cache control methods to notify external logic */ >>> + if (processor_pm_external()) >>> + memcpy(&cx.reg, reg, sizeof(*reg)); >>> +#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ >>> >>> >> This #ifdef should be unnecessary. >> > > This "#ifdef" is the counterpart of the following patch. The cx.reg definition is embraced by CONFIG_PROCESSOR_EXTERNAL_CONTROL, so the code manipulating on the cx.reg also need "#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL" >I see. In that case it might be better to wrap the memcpy up with an inline function which can be defined either way, to keep the #ifdef out of the code itself. J _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jeremy Fitzhardinge
2009-Jul-30 20:52 UTC
Re: [Xen-devel] [PATCH][pvops_dom0][2/4] Introduce the external control operation interface for domain0 ACPI parser
On 07/30/09 08:37, Len Brown wrote:> I agree with Kevin that it would be a mistake to put ACPI both into > both dom0 and the hypervisor. Frankly, on many levels, ACPI was > designed with Windows in mind, and the further an OS strays from > how Windows does things, the less likely you''ll run well on many > systems. Obviously, Xen looks nothing like windows, or any other OS, > for it seems to have not one division between implementation and > policy, but multiple... > > So I have a fundamental lack of understanding of the logic > behind the partitioning behind the hypervisor and dom0. > Maybe somebody explain it to me in terms that I''ll understand? >The basic idea is that Xen controls the things it must, and leaves everything else to guest kernels. At heart that means it controls the physical CPUs (which includes things like local APICs) and memory (by maintaining control over the CPU''s paging hardware via the pagetables). Everything beyond that is left to guest domains which handle various responsibilities; informally we refer to "dom0" which is "the" privileged domain which handles things like hardware discovery, device drivers, domain creation, and a number of other services. However there''s no inherent reason why all these jobs must be aggregated together. For example, there''s active work on having specific "driver domains" which have responsibility for a specific piece of hardware or class of hardware, but don''t (and can''t) do any of the other "privileged" jobs.> It reminds me of the partitioning between the Mach microkernel > and the "user-space OS personality, eg Unix". This looked really neat > in proposals for funding and academic papers, but in reality it turned > out to have little value other than employing programmers to > re-invent the wheel, only to discover that the original round > wheel was better than the square one that they produced... >There are some parallels, and there''s even a paper with a title something like "hypervisors: microkernels done right". I think the rough sketch of the argument is that a Mach-like system with lots of server processes fails in practise because its a poor match for the APIs that people actually want to use and are used to, and if you want Unix-like functionality then you just need to put a Unix in there. Xen''s breakdown is more along the lines of the hardware architecture itself, and therefore is in some sense more "natural": kernels which run processes are real kernels, with the full programming interfaces everyone wants to use; shared resources like CPU and memory can be multiplexed fairly easily using well-understood techniques; with some extra work, you can even let guest domains have direct hardware access using their normal device drivers so that the hypervisor doesn''t need to deal with them. ACPI - which wears many hats affecting many aspects of the system - doesn''t have any neat dividing lines, and doesn''t follow the contours of the underlying architecture, and so appears to be a poor match for a Xen-like model. If it were possible to partition ACPI into broad function groupings, then maybe it would be possible to get a better fit, but I''m not sure whether that''s possible. In general Xen doesn''t have much use for ACPI; aside from this specific case of needing to know how to control the CPUs for power management, it doesn''t really care about ACPI''s services. It doesn''t do interrupt routing, it doesn''t really need to know about thermal management, any anything it does need to know it can be told by the kernel which does have a much greater interest in ACPI. S3 suspend/resume needs to go via Xen for the final stage, but aside from that it can all be handled in Linux. J _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel