Jiang, Yunhong
2009-Nov-06 08:20 UTC
[Xen-devel] [PATCH][DOM0] Expose physical CPU information in dom0
This patch expose host''s physical CPU information to dom0 in sysfs, so that dom0''s management tools can control the physical CPU if needed. It depends on xen hypervisor''s changes to works properly. It also provides interface in sysfs to logical online/offline a physical CPU. Notice: The pCPU information in dom0 is synced with xen hypervisor asynchronously. Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com> --jyh drivers/xen/Makefile | 2 +- drivers/xen/pcpu.c | 425 ++++++++++++++++++++++++++++++++++++++ include/xen/interface/platform.h | 39 ++++ include/xen/interface/xen.h | 1 + include/xen/pcpu.h | 29 +++ 5 files changed, 495 insertions(+), 1 deletions(-) create mode 100644 drivers/xen/pcpu.c create mode 100644 include/xen/pcpu.h diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index cddfffb..5d01856 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,4 +1,4 @@ -obj-y += grant-table.o features.o events.o manage.o biomerge.o +obj-y += grant-table.o features.o events.o manage.o biomerge.o pcpu.o obj-y += xenbus/ live_maps.o nostackp := $(call cc-option, -fno-stack-protector) diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c new file mode 100644 index 0000000..631660e --- /dev/null +++ b/drivers/xen/pcpu.c @@ -0,0 +1,425 @@ +/* + * pcpu.c - management physical cpu in dom0 environment + */ +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> +#include <linux/cpu.h> +#include <xen/xenbus.h> +#include <xen/pcpu.h> +#include <xen/events.h> +#include <xen/acpi.h> + +static struct sysdev_class xen_pcpu_sysdev_class = { + .name = "xen_pcpu", +}; + +static DEFINE_SPINLOCK(xen_pcpu_spinlock); +static RAW_NOTIFIER_HEAD(xen_pcpu_chain); + +/* No need for irq disable since hotplug notify is in workqueue context */ +#define get_pcpu_lock() spin_lock(&xen_pcpu_spinlock); +#define put_pcpu_lock() spin_unlock(&xen_pcpu_spinlock); + +struct xen_pcpus { + struct list_head list; + int possible; + int present; +}; +static struct xen_pcpus xen_pcpus; + +int register_xen_pcpu_notifier(struct notifier_block *nb) +{ + int ret; + + /* All refer to the chain notifier is protected by the pcpu_lock */ + get_pcpu_lock(); + ret = raw_notifier_chain_register(&xen_pcpu_chain, nb); + put_pcpu_lock(); + return ret; +} +EXPORT_SYMBOL_GPL(register_xen_pcpu_notifier); + +void unregister_xen_pcpu_notifier(struct notifier_block *nb) +{ + get_pcpu_lock(); + raw_notifier_chain_unregister(&xen_pcpu_chain, nb); + put_pcpu_lock(); +} +EXPORT_SYMBOL_GPL(unregister_xen_pcpu_notifier); + +static int xen_pcpu_down(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_resource_hotplug, + .interface_version = XENPF_INTERFACE_VERSION, + .u.resource.u.cpu_ol.cpuid = xen_id, + }; + + op.u.resource.sub_cmd = XEN_CPU_offline; + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static int xen_pcpu_up(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_resource_hotplug, + .interface_version = XENPF_INTERFACE_VERSION, + .u.resource.u.cpu_ol.cpuid = xen_id, + }; + + op.u.resource.sub_cmd = XEN_CPU_online; + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static ssize_t show_online(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE)); +} + +static ssize_t __ref store_online(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + ssize_t ret; + + switch (buf[0]) { + case ''0'': + ret = xen_pcpu_down(cpu->xen_id); + break; + case ''1'': + ret = xen_pcpu_up(cpu->xen_id); + break; + default: + ret = -EINVAL; + } + + if (ret >= 0) + ret = count; + return ret; +} + +static SYSDEV_ATTR(online, 0644, show_online, store_online); + +static int xen_pcpu_free(struct pcpu *pcpu) +{ + if (!pcpu) + return 0; + + sysdev_remove_file(&pcpu->sysdev, &attr_online); + sysdev_unregister(&pcpu->sysdev); + list_del(&pcpu->pcpu_list); + kfree(pcpu); + + return 0; +} + +static struct pcpu *xen_pcpu_add(struct xen_physical_cpuinfo *info) +{ + struct pcpu *cpu; + int error; + + printk(KERN_DEBUG "xen_pcpu_add: xen_id %x apic_id %x acpi_id %x\n", + info->xen_cpuid, info->apic_id, info->acpi_id); + cpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL); + if (!cpu) + return NULL; + + INIT_LIST_HEAD(&cpu->pcpu_list); + cpu->xen_id = info->xen_cpuid; + cpu->apic_id = info->apic_id; + cpu->acpi_id = info->acpi_id; + cpu->flags = info->flags; + + cpu->sysdev.cls = &xen_pcpu_sysdev_class; + cpu->sysdev.id = info->xen_cpuid; + + error = sysdev_register(&cpu->sysdev); + if (error) { + printk(KERN_WARNING "xen_pcpu_add: Failed to register pcpu\n"); + kfree(cpu); + return NULL; + } + sysdev_create_file(&cpu->sysdev, &attr_online); + if (error) { + printk(KERN_WARNING "xen_pcpu_add: Failed to create attr\n"); + sysdev_unregister(&cpu->sysdev); + kfree(cpu); + return NULL; + } + list_add_tail(&cpu->pcpu_list, &xen_pcpus.list); + + return cpu; +} + +static struct xen_physical_cpuinfo *xen_pcpu_info_fetch(int *num, + int *possible) +{ + int cpu_num, ret = 0; + struct xen_physical_cpuinfo *info; + xen_platform_op_t op = { + .cmd = XENPF_get_cpuinfo, + .interface_version = XENPF_INTERFACE_VERSION, + .u.pcpu_info.ncpus = 0, + }; + + set_xen_guest_handle(op.u.pcpu_info.info, NULL); + + ret = HYPERVISOR_dom0_op(&op); + if (ret) + return NULL; + + cpu_num = op.u.pcpu_info.max_cpus; + + info = kzalloc(cpu_num * sizeof(struct xen_physical_cpuinfo), + GFP_KERNEL); + if (!info) + return NULL; + + op.u.pcpu_info.ncpus = cpu_num; + set_xen_guest_handle(op.u.pcpu_info.info, info); + + ret = HYPERVISOR_dom0_op(&op); + if (ret) { + kfree(info); + printk(KERN_WARNING "xen_pcpu_info_fetch: Error fetch pcpu info\n"); + return NULL; + } + + if (possible) + *possible = op.u.pcpu_info.max_cpus; + if (num) + *num = op.u.pcpu_info.ncpus; + + return info; +} + +static inline int same_pcpu(struct xen_physical_cpuinfo *info, + struct pcpu *pcpu) +{ + return (pcpu->apic_id == info->apic_id) && + (pcpu->xen_id == info->xen_cpuid); +} + +/* + * Return 1 if online status changed + */ +static int xen_pcpu_online_check(struct xen_physical_cpuinfo *info, + struct pcpu *pcpu) +{ + int result = 0; + + if (!same_pcpu(info, pcpu)) + return 0; + + if (xen_pcpu_online(info->flags) && !xen_pcpu_online(pcpu->flags)) { + /* the pcpu is onlined */ + pcpu->flags |= XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_ONLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ONLINE, (void *)(long)pcpu->xen_id); + result = 1; + } else if (!xen_pcpu_online(info->flags) && + xen_pcpu_online(pcpu->flags)) { + /* The pcpu is offlined now */ + pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_OFFLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_OFFLINE, (void *)(long)pcpu->xen_id); + result = 1; + } + + return result; +} + +/* + * Sync dom0''s pcpu information with xen hypervisor''s + */ +static int xen_pcpu_sync(void) +{ + struct xen_physical_cpuinfo *info; + int cpu_num, i, ret = -1; + struct list_head *elem, *tmp; + struct pcpu *pcpu; + + info = xen_pcpu_info_fetch(&cpu_num, NULL); + if (!info) + return -1; + + get_pcpu_lock(); + + /* Check for current cpu list */ + for (i = 0; i < cpu_num; i++) { + int found = 0; + + list_for_each_entry(pcpu, &xen_pcpus.list, pcpu_list) { + if (same_pcpu(&info[i], pcpu)) { + xen_pcpu_online_check(&info[i], pcpu); + pcpu->status |= PCPU_LOOPED; + found = 1; + } + } + if (!found) { + struct pcpu *cpu; + + /* + * Normally a physical cpu is onlined in two step + * Firstly, the physical CPU is hot-added, secondly + * it will be take online through sysfs writing + * So a hot-added CPU should be offlined initially + */ + if (xen_pcpu_online(info[i].flags)) + printk(KERN_WARNING "xen_pcpu_sync:" + "A hotadd cpu is onlined also\n"); + + cpu = xen_pcpu_add(&info[i]); + if (cpu == NULL) + goto failed; + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ADD, + (void *)(long)cpu->xen_id); + cpu->status |= PCPU_LOOPED; + } + } + + list_for_each_safe(elem, tmp, &xen_pcpus.list) { + pcpu = list_entry(elem, struct pcpu, pcpu_list); + if (pcpu->status & PCPU_LOOPED) + pcpu->status &= ~PCPU_LOOPED; + else { + /* The pcpu does not exist any more, remove it */ + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_REMOVE, + (void *)(long)pcpu->xen_id); + xen_pcpu_free(pcpu); + } + } + + ret = 0; +failed: + put_pcpu_lock(); + kfree(info); + return ret; +} + +static int __init xen_pcpu_info_init(void) +{ + int possible, cpu_num, i; + struct xen_physical_cpuinfo *info = NULL; + struct list_head *elem, *tmp; + struct pcpu *pcpu; + + info = xen_pcpu_info_fetch(&cpu_num, &possible); + if (!info) { + printk(KERN_WARNING + "xen_pcpu_info_init: Failed to fetch pcpu_info\n"); + return -1; + } + + get_pcpu_lock(); + + INIT_LIST_HEAD(&xen_pcpus.list); + xen_pcpus.possible = possible; + xen_pcpus.present = cpu_num; + + for (i = 0; i < cpu_num; i++) { + pcpu = xen_pcpu_add(&info[i]); + if (!pcpu) + goto failed; + } + + put_pcpu_lock(); + kfree(info); + + return 0; +failed: + list_for_each_safe(elem, tmp, &xen_pcpus.list) { + pcpu = list_entry(elem, struct pcpu, pcpu_list); + xen_pcpu_free(pcpu); + } + xen_pcpus.possible = xen_pcpus.present = 0; + INIT_LIST_HEAD(&xen_pcpus.list); + put_pcpu_lock(); + + kfree(info); + return -1; +} + +static void xen_pcpu_dpc(struct work_struct *work) +{ + if (xen_pcpu_sync() < 0) + printk(KERN_WARNING + "xen_pcpu_dpc: Failed to sync pcpu information\n"); +} +static DECLARE_WORK(xen_pcpu_work, xen_pcpu_dpc); + +/* + * type: 0 add, 1 remove + */ +int xen_pcpu_hotplug(int type, uint32_t apic_id) +{ + struct pcpu *pcpu; + int found = 0; + + xen_pcpu_sync(); + get_pcpu_lock(); + list_for_each_entry(pcpu, &xen_pcpus.list, pcpu_list) + { + if (pcpu->apic_id == apic_id) { + found = 1; + break; + } + } + put_pcpu_lock(); + + if (!found && (type == HOTPLUG_TYPE_ADD)) + printk(KERN_WARNING "The cpu is not added into Xen HV?\n"); + + if (found && (type == HOTPLUG_TYPE_REMOVE)) + printk(KERN_WARNING "The cpu still exits in Xen HV?\n"); + return 0; +} +EXPORT_SYMBOL(xen_pcpu_hotplug); + +static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id) +{ + schedule_work(&xen_pcpu_work); + return IRQ_HANDLED; +} + +static int __init xen_pcpu_init(void) +{ + int err; + + if (!xen_initial_domain()) + return 0; + + err = sysdev_class_register(&xen_pcpu_sysdev_class); + if (err) { + printk(KERN_WARNING + "xen_pcpu_init: register xen_pcpu sysdev Failed!\n"); + return err; + } + + err = xen_pcpu_info_init(); + if (!err) + err = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, + 0, xen_pcpu_interrupt, 0, "pcpu", NULL); + if (err < 0) + printk(KERN_WARNING "xen_pcpu_init: " + "Failed to bind pcpu_state virq\n" + "You will lost latest information! \n"); + return err; +} + +subsys_initcall(xen_pcpu_init); diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h index 6783fce..1a5385a 100644 --- a/include/xen/interface/platform.h +++ b/include/xen/interface/platform.h @@ -312,6 +312,43 @@ struct xenpf_set_processor_pminfo { typedef struct xenpf_set_processor_pminfo xenpf_set_processor_pminfo_t; DEFINE_GUEST_HANDLE_STRUCT(xenpf_set_processor_pminfo); +#define XENPF_get_cpuinfo 55 +struct xen_physical_cpuinfo { + uint32_t xen_cpuid; + uint32_t apic_id; + uint32_t acpi_id; +#define XEN_PCPU_FLAGS_ONLINE 1 + uint32_t flags; +}; +typedef struct xen_physical_cpuinfo xen_physical_cpuinfo_t; +DEFINE_GUEST_HANDLE_STRUCT(xen_physical_cpuinfo); + +struct xenpf_pcpu_info { + /* IN/OUT */ + uint32_t ncpus; + /* OUT */ + /* The possible CPU */ + uint32_t max_cpus; + GUEST_HANDLE(xen_physical_cpuinfo) info; +}; +typedef struct xenpf_pcpu_info xenpf_pcpu_info_t; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_pcpu_info); + +struct xenpf_cpu_ol { + uint32_t cpuid; +}; + +#define XENPF_resource_hotplug 56 +struct xenpf_resource_hotplug { + uint32_t sub_cmd; +#define XEN_CPU_online 1 +#define XEN_CPU_offline 2 + union { + struct xenpf_cpu_ol cpu_ol; + } u; +}; + + struct xen_platform_op { uint32_t cmd; uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ @@ -327,6 +364,8 @@ struct xen_platform_op { struct xenpf_change_freq change_freq; struct xenpf_getidletime getidletime; struct xenpf_set_processor_pminfo set_pminfo; + struct xenpf_pcpu_info pcpu_info; + struct xenpf_resource_hotplug resource; uint8_t pad[128]; } u; }; diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 812ffd5..9ffaee0 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -79,6 +79,7 @@ #define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */ #define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */ #define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */ +#define VIRQ_PCPU_STATE 9 /* (DOM0) PCPU state changed */ /* Architecture-specific VIRQ definitions. */ #define VIRQ_ARCH_0 16 diff --git a/include/xen/pcpu.h b/include/xen/pcpu.h new file mode 100644 index 0000000..81478d4 --- /dev/null +++ b/include/xen/pcpu.h @@ -0,0 +1,29 @@ +#ifndef _XEN_PCPU_H +#define _XEN_PCPU_H + +extern int xen_pcpu_hotplug(int type, uint32_t apic_id); +#define XEN_PCPU_ONLINE 0x01 +#define XEN_PCPU_OFFLINE 0x02 +#define XEN_PCPU_ADD 0x04 +#define XEN_PCPU_REMOVE 0x08 + +struct pcpu { + struct list_head pcpu_list; + struct sys_device sysdev; + uint32_t xen_id; + uint32_t apic_id; + uint32_t acpi_id; + uint32_t flags; +#define PCPU_LOOPED 0x10000000 + uint32_t status; +}; + +static inline int xen_pcpu_online(uint32_t flags) +{ + return !!(flags & XEN_PCPU_FLAGS_ONLINE); +} + +extern int register_xen_pcpu_notifier(struct notifier_block *nb); + +extern void unregister_xen_pcpu_notifier(struct notifier_block *nb); +#endif -- 1.5.4.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jiang, Yunhong
2009-Nov-12 15:38 UTC
[Xen-devel] RE: [PATCH][DOM0] Expose physical CPU information in dom0
Jeremy, this patch is based on the new hypervisor interface, please have a look. Export host physical CPU information to dom0 This patch expose host''s physical CPU information to dom0 in sysfs, so that dom0''s management tools can control the physical CPU if needed. It also provides interface in sysfs to logical online/offline a physical CPU. Notice: The information in dom0 is synced with xen hypervisor asynchronously. Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com> --- drivers/xen/Makefile | 2 drivers/xen/pcpu.c | 449 ++++++++++++++++++++++++++++++++++++++ include/xen/interface/platform.h | 27 ++ include/xen/interface/xen.h | 1 include/xen/pcpu.h | 30 +++ 5 files changed, 508 insertions(+), 1 deletions(-) create mode 100644 drivers/xen/pcpu.c create mode 100644 include/xen/pcpu.h diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index cddfffb..5d01856 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,4 +1,4 @@ -obj-y += grant-table.o features.o events.o manage.o biomerge.o +obj-y += grant-table.o features.o events.o manage.o biomerge.o pcpu.o obj-y += xenbus/ live_maps.o nostackp := $(call cc-option, -fno-stack-protector) diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c new file mode 100644 index 0000000..9c45f70 --- /dev/null +++ b/drivers/xen/pcpu.c @@ -0,0 +1,449 @@ +/* + * pcpu.c - management physical cpu in dom0 environment + */ +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> +#include <linux/cpu.h> +#include <xen/xenbus.h> +#include <xen/pcpu.h> +#include <xen/events.h> +#include <xen/acpi.h> + +static struct sysdev_class xen_pcpu_sysdev_class = { + .name = "xen_pcpu", +}; + +static DEFINE_SPINLOCK(xen_pcpu_spinlock); +static RAW_NOTIFIER_HEAD(xen_pcpu_chain); + +/* No need for irq disable since hotplug notify is in workqueue context */ +#define get_pcpu_lock() spin_lock(&xen_pcpu_spinlock); +#define put_pcpu_lock() spin_unlock(&xen_pcpu_spinlock); + +struct xen_pcpus { + struct list_head list; + int present; +}; +static struct xen_pcpus xen_pcpus; + +int register_xen_pcpu_notifier(struct notifier_block *nb) +{ + int ret; + + /* All refer to the chain notifier is protected by the pcpu_lock */ + get_pcpu_lock(); + ret = raw_notifier_chain_register(&xen_pcpu_chain, nb); + put_pcpu_lock(); + return ret; +} +EXPORT_SYMBOL_GPL(register_xen_pcpu_notifier); + +void unregister_xen_pcpu_notifier(struct notifier_block *nb) +{ + get_pcpu_lock(); + raw_notifier_chain_unregister(&xen_pcpu_chain, nb); + put_pcpu_lock(); +} +EXPORT_SYMBOL_GPL(unregister_xen_pcpu_notifier); + +static int xen_pcpu_down(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_cpu_offline, + .interface_version = XENPF_INTERFACE_VERSION, + .u.cpu_ol.cpuid = xen_id, + }; + + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static int xen_pcpu_up(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_cpu_online, + .interface_version = XENPF_INTERFACE_VERSION, + .u.cpu_ol.cpuid = xen_id, + }; + + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static ssize_t show_online(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE)); +} + +static ssize_t __ref store_online(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + ssize_t ret; + + switch (buf[0]) { + case ''0'': + ret = xen_pcpu_down(cpu->xen_id); + break; + case ''1'': + ret = xen_pcpu_up(cpu->xen_id); + break; + default: + ret = -EINVAL; + } + + if (ret >= 0) + ret = count; + return ret; +} + +static SYSDEV_ATTR(online, 0644, show_online, store_online); + +static ssize_t show_apicid(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", cpu->apic_id); +} + +static ssize_t show_acpiid(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", cpu->acpi_id); +} +static SYSDEV_ATTR(apic_id, 0444, show_apicid, NULL); +static SYSDEV_ATTR(acpi_id, 0444, show_acpiid, NULL); + +static int xen_pcpu_free(struct pcpu *pcpu) +{ + if (!pcpu) + return 0; + + sysdev_remove_file(&pcpu->sysdev, &attr_online); + sysdev_unregister(&pcpu->sysdev); + list_del(&pcpu->pcpu_list); + kfree(pcpu); + + return 0; +} + +static inline int same_pcpu(struct xenpf_pcpuinfo *info, + struct pcpu *pcpu) +{ + return (pcpu->apic_id == info->apic_id) && + (pcpu->xen_id == info->xen_cpuid); +} + +/* + * Return 1 if online status changed + */ +static int xen_pcpu_online_check(struct xenpf_pcpuinfo *info, + struct pcpu *pcpu) +{ + int result = 0; + + if (info->xen_cpuid != pcpu->xen_id) + return 0; + + if (xen_pcpu_online(info->flags) && !xen_pcpu_online(pcpu->flags)) { + /* the pcpu is onlined */ + pcpu->flags |= XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_ONLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ONLINE, (void *)(long)pcpu->xen_id); + result = 1; + } else if (!xen_pcpu_online(info->flags) && + xen_pcpu_online(pcpu->flags)) { + /* The pcpu is offlined now */ + pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_OFFLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_OFFLINE, (void *)(long)pcpu->xen_id); + result = 1; + } + + return result; +} + +int pcpu_sysdev_init(struct pcpu *cpu) +{ + int error; + + error = sysdev_register(&cpu->sysdev); + if (error) { + printk(KERN_WARNING "xen_pcpu_add: Failed to register pcpu\n"); + kfree(cpu); + return -1; + } + sysdev_create_file(&cpu->sysdev, &attr_online); + sysdev_create_file(&cpu->sysdev, &attr_apic_id); + sysdev_create_file(&cpu->sysdev, &attr_acpi_id); + return 0; +} + +static struct pcpu *get_pcpu(int xen_id) +{ + struct pcpu *pcpu = NULL; + + list_for_each_entry(pcpu, &xen_pcpus.list, pcpu_list) { + if (pcpu->xen_id == xen_id) + break; + } + return (pcpu->xen_id == xen_id) ? pcpu : NULL; +} + +static struct pcpu *init_pcpu(struct xenpf_pcpuinfo *info) +{ + struct pcpu *pcpu; + + if (info->flags & XEN_PCPU_FLAGS_INVALID) + return NULL; + + /* The PCPU is just added */ + pcpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL); + if (!pcpu) + return NULL; + + INIT_LIST_HEAD(&pcpu->pcpu_list); + pcpu->xen_id = info->xen_cpuid; + pcpu->apic_id = info->apic_id; + pcpu->acpi_id = info->acpi_id; + pcpu->flags = info->flags; + + pcpu->sysdev.cls = &xen_pcpu_sysdev_class; + pcpu->sysdev.id = info->xen_cpuid; + + if (pcpu_sysdev_init(pcpu)) { + kfree(pcpu); + return NULL; + } + + list_add_tail(&pcpu->pcpu_list, &xen_pcpus.list); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ADD, + (void *)(long)pcpu->xen_id); + return pcpu; +} + +#define PCPU_ADDED 1 +#define PCPU_ONLINE_OFFLINE 2 +#define PCPU_REMOVED 3 +/* + * Caller should hold the pcpu lock + * < 0: Something wrong + * 0: No changes + * > 0: State changed + */ +static struct pcpu *_sync_pcpu(int cpu_num, int *max_id, int *result) +{ + struct pcpu *pcpu = NULL; + struct xenpf_pcpuinfo *info; + xen_platform_op_t op = { + .cmd = XENPF_get_cpuinfo, + .interface_version = XENPF_INTERFACE_VERSION, + }; + int ret; + + *result = -1; + + info = &op.u.pcpu_info; + info->xen_cpuid = cpu_num; + + ret = HYPERVISOR_dom0_op(&op); + if (ret) + return NULL; + + if (max_id) + *max_id = op.u.pcpu_info.max_present; + + pcpu = get_pcpu(cpu_num); + + if (info->flags & XEN_PCPU_FLAGS_INVALID) { + /* The pcpu has been removed */ + *result = 0; + if (pcpu) { + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_REMOVE, + (void *)(long)pcpu->xen_id); + xen_pcpu_free(pcpu); + *result = PCPU_REMOVED; + } + return NULL; + } + + + if (!pcpu) { + *result = PCPU_ADDED; + pcpu = init_pcpu(info); + if (pcpu == NULL) { + printk(KERN_WARNING "Failed to init pcpu %x\n", + info->xen_cpuid); + *result = -1; + } + } else { + *result = 0; + /* + * Old PCPU is replaced with a new pcpu, this means + * several virq is missed, will it happen? + */ + if (!same_pcpu(info, pcpu)) { + printk(KERN_WARNING "Pcpu %x changed!\n", + pcpu->xen_id); + pcpu->apic_id = info->apic_id; + pcpu->acpi_id = info->acpi_id; + } + if (xen_pcpu_online_check(info, pcpu)) + *result = PCPU_ONLINE_OFFLINE; + } + return pcpu; +} + +static struct pcpu *sync_pcpu(int xen_id, int *result) +{ + struct pcpu *pcpu; + + get_pcpu_lock(); + pcpu = _sync_pcpu(xen_id, NULL, result); + put_pcpu_lock(); + + return pcpu; +} +/* + * Sync dom0''s pcpu information with xen hypervisor''s + */ +static int xen_sync_pcpus(void) +{ + /* + * Boot cpu always have cpu_id 0 in xen + */ + int cpu_num = 0, max_id = 0, result = 0, present = 0; + struct list_head *elem, *tmp; + struct pcpu *pcpu; + + get_pcpu_lock(); + + while ((result >= 0) && (cpu_num <= max_id)) { + pcpu = _sync_pcpu(cpu_num, &max_id, &result); + + printk(KERN_DEBUG "sync cpu %x get result %x max_id %x\n", + cpu_num, result, max_id); + + switch (result) { + case 0: + if (pcpu) + present++; + break; + case PCPU_ADDED: + case PCPU_ONLINE_OFFLINE: + present++; + case PCPU_REMOVED: + break; + default: + printk(KERN_WARNING "Failed to sync pcpu %x\n", + cpu_num); + break; + + } + cpu_num++; + } + + if (result < 0) { + list_for_each_safe(elem, tmp, &xen_pcpus.list) { + pcpu = list_entry(elem, struct pcpu, pcpu_list); + xen_pcpu_free(pcpu); + } + } + + xen_pcpus.present = present; + put_pcpu_lock(); + + return 0; +} + +static void xen_pcpu_dpc(struct work_struct *work) +{ + if (xen_sync_pcpus() < 0) + printk(KERN_WARNING + "xen_pcpu_dpc: Failed to sync pcpu information\n"); +} +static DECLARE_WORK(xen_pcpu_work, xen_pcpu_dpc); + +/* + * type: 0 add, 1 remove + */ +int xen_pcpu_hotplug(int type, uint32_t apic_id) +{ + struct pcpu *pcpu; + int found = 0; + + xen_sync_pcpus(); + + get_pcpu_lock(); + list_for_each_entry(pcpu, &xen_pcpus.list, pcpu_list) + { + if (pcpu->apic_id == apic_id) { + found = 1; + break; + } + } + put_pcpu_lock(); + + + if (!found && (type == HOTPLUG_TYPE_ADD)) + printk(KERN_WARNING "The cpu is not added into Xen HV?\n"); + + if (found && (type == HOTPLUG_TYPE_REMOVE)) + printk(KERN_WARNING "The cpu still exits in Xen HV?\n"); + return 0; +} +EXPORT_SYMBOL(xen_pcpu_hotplug); + +static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id) +{ + schedule_work(&xen_pcpu_work); + return IRQ_HANDLED; +} + +static int __init xen_pcpu_init(void) +{ + int err; + + if (!xen_initial_domain()) + return 0; + + err = sysdev_class_register(&xen_pcpu_sysdev_class); + if (err) { + printk(KERN_WARNING + "xen_pcpu_init: register xen_pcpu sysdev Failed!\n"); + return err; + } + + INIT_LIST_HEAD(&xen_pcpus.list); + xen_pcpus.present = 0; + + xen_sync_pcpus(); + if (xen_pcpus.present > 0) + err = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, + 0, xen_pcpu_interrupt, 0, "pcpu", NULL); + if (err < 0) + printk(KERN_WARNING "xen_pcpu_init: " + "Failed to bind pcpu_state virq\n" + "You will lost latest information! \n"); + return err; +} + +subsys_initcall(xen_pcpu_init); diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h index 6783fce..14df81d 100644 --- a/include/xen/interface/platform.h +++ b/include/xen/interface/platform.h @@ -312,6 +312,31 @@ struct xenpf_set_processor_pminfo { typedef struct xenpf_set_processor_pminfo xenpf_set_processor_pminfo_t; DEFINE_GUEST_HANDLE_STRUCT(xenpf_set_processor_pminfo); +#define XENPF_get_cpuinfo 55 +struct xenpf_pcpuinfo { + /* IN */ + uint32_t xen_cpuid; + /* OUT */ + /* The maxium cpu_id that is present */ + uint32_t max_present; +#define XEN_PCPU_FLAGS_ONLINE 1 + /* Correponding xen_cpuid is not present*/ +#define XEN_PCPU_FLAGS_INVALID 2 + uint32_t flags; + uint32_t apic_id; + uint32_t acpi_id; +}; +typedef struct xenpf_pcpuinfo xenpf_pcpuinfo_t; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_pcpuinfo_t); + +#define XENPF_cpu_online 56 +#define XENPF_cpu_offline 57 +struct xenpf_cpu_ol { + uint32_t cpuid; +}; +typedef struct xenpf_cpu_ol xenpf_cpu_ol_t; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_cpu_ol_t); + struct xen_platform_op { uint32_t cmd; uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ @@ -327,6 +352,8 @@ struct xen_platform_op { struct xenpf_change_freq change_freq; struct xenpf_getidletime getidletime; struct xenpf_set_processor_pminfo set_pminfo; + struct xenpf_pcpuinfo pcpu_info; + struct xenpf_cpu_ol cpu_ol; uint8_t pad[128]; } u; }; diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 812ffd5..9ffaee0 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -79,6 +79,7 @@ #define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */ #define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */ #define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */ +#define VIRQ_PCPU_STATE 9 /* (DOM0) PCPU state changed */ /* Architecture-specific VIRQ definitions. */ #define VIRQ_ARCH_0 16 diff --git a/include/xen/pcpu.h b/include/xen/pcpu.h new file mode 100644 index 0000000..fb2bf6b --- /dev/null +++ b/include/xen/pcpu.h @@ -0,0 +1,30 @@ +#ifndef _XEN_PCPU_H +#define _XEN_PCPU_H + +#include <xen/interface/platform.h> +#include <linux/sysdev.h> + +extern int xen_pcpu_hotplug(int type, uint32_t apic_id); +#define XEN_PCPU_ONLINE 0x01 +#define XEN_PCPU_OFFLINE 0x02 +#define XEN_PCPU_ADD 0x04 +#define XEN_PCPU_REMOVE 0x08 + +struct pcpu { + struct list_head pcpu_list; + struct sys_device sysdev; + uint32_t xen_id; + uint32_t apic_id; + uint32_t acpi_id; + uint32_t flags; +}; + +static inline int xen_pcpu_online(uint32_t flags) +{ + return !!(flags & XEN_PCPU_FLAGS_ONLINE); +} + +extern int register_xen_pcpu_notifier(struct notifier_block *nb); + +extern void unregister_xen_pcpu_notifier(struct notifier_block *nb); +#endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Konrad Rzeszutek Wilk
2009-Nov-12 15:46 UTC
Re: [Xen-devel] RE: [PATCH][DOM0] Expose physical CPU information in dom0
Some little comments.. .. snip ..> + if (info->flags & XEN_PCPU_FLAGS_INVALID) { > + /* The pcpu has been removed */ > + *result = 0;You use #defines for the other cases. Should there be one for 0? Is 0 PCPU_REMOVED?> + if (pcpu) { > + raw_notifier_call_chain(&xen_pcpu_chain, > + XEN_PCPU_REMOVE, > + (void *)(long)pcpu->xen_id); > + xen_pcpu_free(pcpu); > + *result = PCPU_REMOVED; > + } > + return NULL; > + } > + > + > + if (!pcpu) { > + *result = PCPU_ADDED; > + pcpu = init_pcpu(info); > + if (pcpu == NULL) { > + printk(KERN_WARNING "Failed to init pcpu %x\n", > + info->xen_cpuid); > + *result = -1;How about #define PCPU_BAD -1? .. snip..> +/* > + * type: 0 add, 1 remove > + */Why not make this an enum?> +int xen_pcpu_hotplug(int type, uint32_t apic_id)_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jiang, Yunhong
2009-Nov-12 16:08 UTC
RE: [Xen-devel] RE: [PATCH][DOM0] Expose physical CPU information in dom0
Konrad Rzeszutek Wilk wrote:> Some little comments.. > > .. snip .. > >> + if (info->flags & XEN_PCPU_FLAGS_INVALID) { >> + /* The pcpu has been removed */ >> + *result = 0; > > You use #defines for the other cases. Should there be one for 0? Is 0 > PCPU_REMOVED?0 means no changes, < 0 means something wrong (like hypercall failure, or memory allocation failed). The defined macro is for state changed (i.e. >0). Yes, we can define 0 as PCPU_NO_CHANGE. For the code above, if the pcpu does not exist before in kernel, it is 0, means no change, otherwise it will be set as PCPU_REMOVED.> >> + if (pcpu) { >> + raw_notifier_call_chain(&xen_pcpu_chain, >> + XEN_PCPU_REMOVE, >> + (void *)(long)pcpu->xen_id); >> + xen_pcpu_free(pcpu); >> + *result = PCPU_REMOVED; >> + } >> + return NULL; >> + } >> + >> + >> + if (!pcpu) { >> + *result = PCPU_ADDED; >> + pcpu = init_pcpu(info); >> + if (pcpu == NULL) { >> + printk(KERN_WARNING "Failed to init pcpu %x\n", + >> info->xen_cpuid); + *result = -1; > > How about #define PCPU_BAD -1?-1 means something wrong, not PCPU bad.> > .. snip.. >> +/* >> + * type: 0 add, 1 remove >> + */ > > Why not make this an enum? > >> +int xen_pcpu_hotplug(int type, uint32_t apic_id)In fact, there is macro for it already. I give the value here to make it more clean. if (!found && (type == HOTPLUG_TYPE_ADD)) printk(KERN_WARNING "The cpu is not added into Xen HV?\n"); if (found && (type == HOTPLUG_TYPE_REMOVE)) printk(KERN_WARNING "The cpu still exits in Xen HV?\n"); return 0; _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jiang, Yunhong
2009-Nov-13 09:42 UTC
RE: [Xen-devel] RE: [PATCH][DOM0] Expose physical CPU information in dom0
Attached is updated version based on Konrad''s feedback. Following changes in this one: a) I change the 0 to "PCPU_NO_CHANGE", and leave -1 as no change as explained prevously. b) change the xen_pcpu_lock to mutex. I didn''t provide enum for xen_pcpu_hotplug''s type parameter, instead, I remove the comments, since I think the code is quite clean already. Jeremy, any comments? And how about other two patches: "Export apic_id for acpi_processor object" and "Add CPU hotplug support to PV_ops dom0", do you have any comments? Thanks Yunhong Jiang This patch expose host''s physical CPU information to dom0 in sysfs, so that dom0''s management tools can control the physical CPU if needed. It also provides interface in sysfs to logical online/offline a physical CPU. Notice: The information in dom0 is synced with xen hypervisor asynchronously. Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com> --- drivers/xen/Makefile | 2 drivers/xen/pcpu.c | 430 ++++++++++++++++++++++++++++++++++++++ include/xen/interface/platform.h | 27 ++ include/xen/interface/xen.h | 1 include/xen/pcpu.h | 30 +++ 5 files changed, 489 insertions(+), 1 deletions(-) create mode 100644 drivers/xen/pcpu.c create mode 100644 include/xen/pcpu.h diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index cddfffb..5d01856 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,4 +1,4 @@ -obj-y += grant-table.o features.o events.o manage.o biomerge.o +obj-y += grant-table.o features.o events.o manage.o biomerge.o pcpu.o obj-y += xenbus/ live_maps.o nostackp := $(call cc-option, -fno-stack-protector) diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c new file mode 100644 index 0000000..ec763de --- /dev/null +++ b/drivers/xen/pcpu.c @@ -0,0 +1,430 @@ +/* + * pcpu.c - management physical cpu in dom0 environment + */ +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> +#include <linux/cpu.h> +#include <xen/xenbus.h> +#include <xen/pcpu.h> +#include <xen/events.h> +#include <xen/acpi.h> + +static struct sysdev_class xen_pcpu_sysdev_class = { + .name = "xen_pcpu", +}; + +static DEFINE_MUTEX(xen_pcpu_lock); +static RAW_NOTIFIER_HEAD(xen_pcpu_chain); + +/* No need for irq disable since hotplug notify is in workqueue context */ +#define get_pcpu_lock() mutex_lock(&xen_pcpu_lock); +#define put_pcpu_lock() mutex_unlock(&xen_pcpu_lock); + +struct xen_pcpus { + struct list_head list; + int present; +}; +static struct xen_pcpus xen_pcpus; + +int register_xen_pcpu_notifier(struct notifier_block *nb) +{ + int ret; + + /* All refer to the chain notifier is protected by the pcpu_lock */ + get_pcpu_lock(); + ret = raw_notifier_chain_register(&xen_pcpu_chain, nb); + put_pcpu_lock(); + return ret; +} +EXPORT_SYMBOL_GPL(register_xen_pcpu_notifier); + +void unregister_xen_pcpu_notifier(struct notifier_block *nb) +{ + get_pcpu_lock(); + raw_notifier_chain_unregister(&xen_pcpu_chain, nb); + put_pcpu_lock(); +} +EXPORT_SYMBOL_GPL(unregister_xen_pcpu_notifier); + +static int xen_pcpu_down(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_cpu_offline, + .interface_version = XENPF_INTERFACE_VERSION, + .u.cpu_ol.cpuid = xen_id, + }; + + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static int xen_pcpu_up(uint32_t xen_id) +{ + int ret; + xen_platform_op_t op = { + .cmd = XENPF_cpu_online, + .interface_version = XENPF_INTERFACE_VERSION, + .u.cpu_ol.cpuid = xen_id, + }; + + ret = HYPERVISOR_dom0_op(&op); + return ret; +} + +static ssize_t show_online(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE)); +} + +static ssize_t __ref store_online(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + ssize_t ret; + + switch (buf[0]) { + case ''0'': + ret = xen_pcpu_down(cpu->xen_id); + break; + case ''1'': + ret = xen_pcpu_up(cpu->xen_id); + break; + default: + ret = -EINVAL; + } + + if (ret >= 0) + ret = count; + return ret; +} + +static SYSDEV_ATTR(online, 0644, show_online, store_online); + +static ssize_t show_apicid(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", cpu->apic_id); +} + +static ssize_t show_acpiid(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + struct pcpu *cpu = container_of(dev, struct pcpu, sysdev); + + return sprintf(buf, "%u\n", cpu->acpi_id); +} +static SYSDEV_ATTR(apic_id, 0444, show_apicid, NULL); +static SYSDEV_ATTR(acpi_id, 0444, show_acpiid, NULL); + +static int xen_pcpu_free(struct pcpu *pcpu) +{ + if (!pcpu) + return 0; + + sysdev_remove_file(&pcpu->sysdev, &attr_online); + sysdev_unregister(&pcpu->sysdev); + list_del(&pcpu->pcpu_list); + kfree(pcpu); + + return 0; +} + +static inline int same_pcpu(struct xenpf_pcpuinfo *info, + struct pcpu *pcpu) +{ + return (pcpu->apic_id == info->apic_id) && + (pcpu->xen_id == info->xen_cpuid); +} + +/* + * Return 1 if online status changed + */ +static int xen_pcpu_online_check(struct xenpf_pcpuinfo *info, + struct pcpu *pcpu) +{ + int result = 0; + + if (info->xen_cpuid != pcpu->xen_id) + return 0; + + if (xen_pcpu_online(info->flags) && !xen_pcpu_online(pcpu->flags)) { + /* the pcpu is onlined */ + pcpu->flags |= XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_ONLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ONLINE, (void *)(long)pcpu->xen_id); + result = 1; + } else if (!xen_pcpu_online(info->flags) && + xen_pcpu_online(pcpu->flags)) { + /* The pcpu is offlined now */ + pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE; + kobject_uevent(&pcpu->sysdev.kobj, KOBJ_OFFLINE); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_OFFLINE, (void *)(long)pcpu->xen_id); + result = 1; + } + + return result; +} + +static int pcpu_sysdev_init(struct pcpu *cpu) +{ + int error; + + error = sysdev_register(&cpu->sysdev); + if (error) { + printk(KERN_WARNING "xen_pcpu_add: Failed to register pcpu\n"); + kfree(cpu); + return -1; + } + sysdev_create_file(&cpu->sysdev, &attr_online); + sysdev_create_file(&cpu->sysdev, &attr_apic_id); + sysdev_create_file(&cpu->sysdev, &attr_acpi_id); + return 0; +} + +static struct pcpu *get_pcpu(int xen_id) +{ + struct pcpu *pcpu = NULL; + + list_for_each_entry(pcpu, &xen_pcpus.list, pcpu_list) { + if (pcpu->xen_id == xen_id) + break; + } + return (pcpu->xen_id == xen_id) ? pcpu : NULL; +} + +static struct pcpu *init_pcpu(struct xenpf_pcpuinfo *info) +{ + struct pcpu *pcpu; + + if (info->flags & XEN_PCPU_FLAGS_INVALID) + return NULL; + + /* The PCPU is just added */ + pcpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL); + if (!pcpu) + return NULL; + + INIT_LIST_HEAD(&pcpu->pcpu_list); + pcpu->xen_id = info->xen_cpuid; + pcpu->apic_id = info->apic_id; + pcpu->acpi_id = info->acpi_id; + pcpu->flags = info->flags; + + pcpu->sysdev.cls = &xen_pcpu_sysdev_class; + pcpu->sysdev.id = info->xen_cpuid; + + if (pcpu_sysdev_init(pcpu)) { + kfree(pcpu); + return NULL; + } + + list_add_tail(&pcpu->pcpu_list, &xen_pcpus.list); + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_ADD, + (void *)(long)pcpu->xen_id); + return pcpu; +} + +#define PCPU_NO_CHANGE 0 +#define PCPU_ADDED 1 +#define PCPU_ONLINE_OFFLINE 2 +#define PCPU_REMOVED 3 +/* + * Caller should hold the pcpu lock + * < 0: Something wrong + * 0: No changes + * > 0: State changed + */ +static struct pcpu *_sync_pcpu(int cpu_num, int *max_id, int *result) +{ + struct pcpu *pcpu = NULL; + struct xenpf_pcpuinfo *info; + xen_platform_op_t op = { + .cmd = XENPF_get_cpuinfo, + .interface_version = XENPF_INTERFACE_VERSION, + }; + int ret; + + *result = -1; + + info = &op.u.pcpu_info; + info->xen_cpuid = cpu_num; + + ret = HYPERVISOR_dom0_op(&op); + if (ret) + return NULL; + + if (max_id) + *max_id = op.u.pcpu_info.max_present; + + pcpu = get_pcpu(cpu_num); + + if (info->flags & XEN_PCPU_FLAGS_INVALID) { + /* The pcpu has been removed */ + *result = PCPU_NO_CHANGE; + if (pcpu) { + raw_notifier_call_chain(&xen_pcpu_chain, + XEN_PCPU_REMOVE, + (void *)(long)pcpu->xen_id); + xen_pcpu_free(pcpu); + *result = PCPU_REMOVED; + } + return NULL; + } + + + if (!pcpu) { + *result = PCPU_ADDED; + pcpu = init_pcpu(info); + if (pcpu == NULL) { + printk(KERN_WARNING "Failed to init pcpu %x\n", + info->xen_cpuid); + *result = -1; + } + } else { + *result = PCPU_NO_CHANGE; + /* + * Old PCPU is replaced with a new pcpu, this means + * several virq is missed, will it happen? + */ + if (!same_pcpu(info, pcpu)) { + printk(KERN_WARNING "Pcpu %x changed!\n", + pcpu->xen_id); + pcpu->apic_id = info->apic_id; + pcpu->acpi_id = info->acpi_id; + } + if (xen_pcpu_online_check(info, pcpu)) + *result = PCPU_ONLINE_OFFLINE; + } + return pcpu; +} + +static struct pcpu *sync_pcpu(int xen_id, int *result) +{ + struct pcpu *pcpu; + + get_pcpu_lock(); + pcpu = _sync_pcpu(xen_id, NULL, result); + put_pcpu_lock(); + + return pcpu; +} +/* + * Sync dom0''s pcpu information with xen hypervisor''s + */ +static int xen_sync_pcpus(void) +{ + /* + * Boot cpu always have cpu_id 0 in xen + */ + int cpu_num = 0, max_id = 0, result = 0, present = 0; + struct list_head *elem, *tmp; + struct pcpu *pcpu; + + get_pcpu_lock(); + + while ((result >= 0) && (cpu_num <= max_id)) { + pcpu = _sync_pcpu(cpu_num, &max_id, &result); + + printk(KERN_DEBUG "sync cpu %x get result %x max_id %x\n", + cpu_num, result, max_id); + + switch (result) { + case PCPU_NO_CHANGE: + if (pcpu) + present++; + break; + case PCPU_ADDED: + case PCPU_ONLINE_OFFLINE: + present++; + case PCPU_REMOVED: + break; + default: + printk(KERN_WARNING "Failed to sync pcpu %x\n", + cpu_num); + break; + + } + cpu_num++; + } + + if (result < 0) { + list_for_each_safe(elem, tmp, &xen_pcpus.list) { + pcpu = list_entry(elem, struct pcpu, pcpu_list); + xen_pcpu_free(pcpu); + } + present = 0; + } + + xen_pcpus.present = present; + + put_pcpu_lock(); + + return 0; +} + +static void xen_pcpu_dpc(struct work_struct *work) +{ + if (xen_sync_pcpus() < 0) + printk(KERN_WARNING + "xen_pcpu_dpc: Failed to sync pcpu information\n"); +} +static DECLARE_WORK(xen_pcpu_work, xen_pcpu_dpc); + +int xen_pcpu_hotplug(int type, uint32_t apic_id) +{ + schedule_work(&xen_pcpu_work); + + return 0; +} +EXPORT_SYMBOL(xen_pcpu_hotplug); + +static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id) +{ + schedule_work(&xen_pcpu_work); + return IRQ_HANDLED; +} + +static int __init xen_pcpu_init(void) +{ + int err; + + if (!xen_initial_domain()) + return 0; + + err = sysdev_class_register(&xen_pcpu_sysdev_class); + if (err) { + printk(KERN_WARNING + "xen_pcpu_init: register xen_pcpu sysdev Failed!\n"); + return err; + } + + INIT_LIST_HEAD(&xen_pcpus.list); + xen_pcpus.present = 0; + + xen_sync_pcpus(); + if (xen_pcpus.present > 0) + err = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, + 0, xen_pcpu_interrupt, 0, "pcpu", NULL); + if (err < 0) + printk(KERN_WARNING "xen_pcpu_init: " + "Failed to bind pcpu_state virq\n" + "You will lost latest information! \n"); + return err; +} + +subsys_initcall(xen_pcpu_init); diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h index 6783fce..14df81d 100644 --- a/include/xen/interface/platform.h +++ b/include/xen/interface/platform.h @@ -312,6 +312,31 @@ struct xenpf_set_processor_pminfo { typedef struct xenpf_set_processor_pminfo xenpf_set_processor_pminfo_t; DEFINE_GUEST_HANDLE_STRUCT(xenpf_set_processor_pminfo); +#define XENPF_get_cpuinfo 55 +struct xenpf_pcpuinfo { + /* IN */ + uint32_t xen_cpuid; + /* OUT */ + /* The maxium cpu_id that is present */ + uint32_t max_present; +#define XEN_PCPU_FLAGS_ONLINE 1 + /* Correponding xen_cpuid is not present*/ +#define XEN_PCPU_FLAGS_INVALID 2 + uint32_t flags; + uint32_t apic_id; + uint32_t acpi_id; +}; +typedef struct xenpf_pcpuinfo xenpf_pcpuinfo_t; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_pcpuinfo_t); + +#define XENPF_cpu_online 56 +#define XENPF_cpu_offline 57 +struct xenpf_cpu_ol { + uint32_t cpuid; +}; +typedef struct xenpf_cpu_ol xenpf_cpu_ol_t; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_cpu_ol_t); + struct xen_platform_op { uint32_t cmd; uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ @@ -327,6 +352,8 @@ struct xen_platform_op { struct xenpf_change_freq change_freq; struct xenpf_getidletime getidletime; struct xenpf_set_processor_pminfo set_pminfo; + struct xenpf_pcpuinfo pcpu_info; + struct xenpf_cpu_ol cpu_ol; uint8_t pad[128]; } u; }; diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 812ffd5..9ffaee0 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -79,6 +79,7 @@ #define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */ #define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */ #define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */ +#define VIRQ_PCPU_STATE 9 /* (DOM0) PCPU state changed */ /* Architecture-specific VIRQ definitions. */ #define VIRQ_ARCH_0 16 diff --git a/include/xen/pcpu.h b/include/xen/pcpu.h new file mode 100644 index 0000000..fb2bf6b --- /dev/null +++ b/include/xen/pcpu.h @@ -0,0 +1,30 @@ +#ifndef _XEN_PCPU_H +#define _XEN_PCPU_H + +#include <xen/interface/platform.h> +#include <linux/sysdev.h> + +extern int xen_pcpu_hotplug(int type, uint32_t apic_id); +#define XEN_PCPU_ONLINE 0x01 +#define XEN_PCPU_OFFLINE 0x02 +#define XEN_PCPU_ADD 0x04 +#define XEN_PCPU_REMOVE 0x08 + +struct pcpu { + struct list_head pcpu_list; + struct sys_device sysdev; + uint32_t xen_id; + uint32_t apic_id; + uint32_t acpi_id; + uint32_t flags; +}; + +static inline int xen_pcpu_online(uint32_t flags) +{ + return !!(flags & XEN_PCPU_FLAGS_ONLINE); +} + +extern int register_xen_pcpu_notifier(struct notifier_block *nb); + +extern void unregister_xen_pcpu_notifier(struct notifier_block *nb); +#endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel