2013-01-25 15:43:34 +08:00
/*
* Copyright ( C ) 2012 Intel Corporation
* Author : Liu Jinsong < jinsong . liu @ intel . com >
* Author : Jiang Yunhong < yunhong . jiang @ 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 , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*/
2013-06-28 03:21:41 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2013-01-25 15:43:34 +08:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/cpu.h>
# include <linux/acpi.h>
# include <linux/uaccess.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# include <acpi/processor.h>
# include <xen/acpi.h>
# include <xen/interface/platform.h>
# include <asm/xen/hypercall.h>
# define PREFIX "ACPI:xen_cpu_hotplug:"
# define INSTALL_NOTIFY_HANDLER 0
# define UNINSTALL_NOTIFY_HANDLER 1
static acpi_status xen_acpi_cpu_hotadd ( struct acpi_processor * pr ) ;
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int xen_acpi_processor_enable ( struct acpi_device * device )
{
acpi_status status = 0 ;
unsigned long long value ;
union acpi_object object = { 0 } ;
struct acpi_buffer buffer = { sizeof ( union acpi_object ) , & object } ;
struct acpi_processor * pr ;
pr = acpi_driver_data ( device ) ;
if ( ! pr ) {
pr_err ( PREFIX " Cannot find driver data \n " ) ;
return - EINVAL ;
}
if ( ! strcmp ( acpi_device_hid ( device ) , ACPI_PROCESSOR_OBJECT_HID ) ) {
/* Declared with "Processor" statement; match ProcessorID */
status = acpi_evaluate_object ( pr - > handle , NULL , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
pr_err ( PREFIX " Evaluating processor object \n " ) ;
return - ENODEV ;
}
pr - > acpi_id = object . processor . proc_id ;
} else {
/* Declared with "Device" statement; match _UID */
status = acpi_evaluate_integer ( pr - > handle , METHOD_NAME__UID ,
NULL , & value ) ;
if ( ACPI_FAILURE ( status ) ) {
pr_err ( PREFIX " Evaluating processor _UID \n " ) ;
return - ENODEV ;
}
pr - > acpi_id = value ;
}
pr - > id = xen_pcpu_id ( pr - > acpi_id ) ;
if ( ( int ) pr - > id < 0 )
/* This cpu is not presented at hypervisor, try to hotadd it */
if ( ACPI_FAILURE ( xen_acpi_cpu_hotadd ( pr ) ) ) {
pr_err ( PREFIX " Hotadd CPU (acpi_id = %d) failed. \n " ,
pr - > acpi_id ) ;
return - ENODEV ;
}
return 0 ;
}
2013-06-19 15:22:41 -04:00
static int xen_acpi_processor_add ( struct acpi_device * device )
2013-01-25 15:43:34 +08:00
{
int ret ;
struct acpi_processor * pr ;
if ( ! device )
return - EINVAL ;
pr = kzalloc ( sizeof ( struct acpi_processor ) , GFP_KERNEL ) ;
if ( ! pr )
return - ENOMEM ;
pr - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , ACPI_PROCESSOR_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_PROCESSOR_CLASS ) ;
device - > driver_data = pr ;
ret = xen_acpi_processor_enable ( device ) ;
if ( ret )
pr_err ( PREFIX " Error when enabling Xen processor \n " ) ;
return ret ;
}
2013-02-24 16:06:13 -08:00
static int xen_acpi_processor_remove ( struct acpi_device * device )
2013-01-25 15:43:34 +08:00
{
struct acpi_processor * pr ;
if ( ! device )
return - EINVAL ;
pr = acpi_driver_data ( device ) ;
if ( ! pr )
return - EINVAL ;
kfree ( pr ) ;
return 0 ;
}
/*--------------------------------------------------------------
Acpi processor hotplug support
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int is_processor_present ( acpi_handle handle )
{
acpi_status status ;
unsigned long long sta = 0 ;
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & sta ) ;
if ( ACPI_SUCCESS ( status ) & & ( sta & ACPI_STA_DEVICE_PRESENT ) )
return 1 ;
/*
* _STA is mandatory for a processor that supports hot plug
*/
if ( status = = AE_NOT_FOUND )
pr_info ( PREFIX " Processor does not support hot plug \n " ) ;
else
pr_info ( PREFIX " Processor Device is not present " ) ;
return 0 ;
}
static int xen_apic_id ( acpi_handle handle )
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
struct acpi_madt_local_apic * lapic ;
int apic_id ;
if ( ACPI_FAILURE ( acpi_evaluate_object ( handle , " _MAT " , NULL , & buffer ) ) )
return - EINVAL ;
if ( ! buffer . length | | ! buffer . pointer )
return - EINVAL ;
obj = buffer . pointer ;
if ( obj - > type ! = ACPI_TYPE_BUFFER | |
obj - > buffer . length < sizeof ( * lapic ) ) {
kfree ( buffer . pointer ) ;
return - EINVAL ;
}
lapic = ( struct acpi_madt_local_apic * ) obj - > buffer . pointer ;
if ( lapic - > header . type ! = ACPI_MADT_TYPE_LOCAL_APIC | |
! ( lapic - > lapic_flags & ACPI_MADT_ENABLED ) ) {
kfree ( buffer . pointer ) ;
return - EINVAL ;
}
apic_id = ( uint32_t ) lapic - > id ;
kfree ( buffer . pointer ) ;
buffer . length = ACPI_ALLOCATE_BUFFER ;
buffer . pointer = NULL ;
return apic_id ;
}
static int xen_hotadd_cpu ( struct acpi_processor * pr )
{
int cpu_id , apic_id , pxm ;
struct xen_platform_op op ;
apic_id = xen_apic_id ( pr - > handle ) ;
if ( apic_id < 0 ) {
pr_err ( PREFIX " Failed to get apic_id for acpi_id %d \n " ,
pr - > acpi_id ) ;
return - ENODEV ;
}
pxm = xen_acpi_get_pxm ( pr - > handle ) ;
if ( pxm < 0 ) {
pr_err ( PREFIX " Failed to get _PXM for acpi_id %d \n " ,
pr - > acpi_id ) ;
return pxm ;
}
op . cmd = XENPF_cpu_hotadd ;
op . u . cpu_add . apic_id = apic_id ;
op . u . cpu_add . acpi_id = pr - > acpi_id ;
op . u . cpu_add . pxm = pxm ;
cpu_id = HYPERVISOR_dom0_op ( & op ) ;
if ( cpu_id < 0 )
pr_err ( PREFIX " Failed to hotadd CPU for acpi_id %d \n " ,
pr - > acpi_id ) ;
return cpu_id ;
}
static acpi_status xen_acpi_cpu_hotadd ( struct acpi_processor * pr )
{
if ( ! is_processor_present ( pr - > handle ) )
return AE_ERROR ;
pr - > id = xen_hotadd_cpu ( pr ) ;
if ( ( int ) pr - > id < 0 )
return AE_ERROR ;
/*
* Sync with Xen hypervisor , providing new / sys / . . . / xen_cpuX
* interface after cpu hotadded .
*/
xen_pcpu_hotplug_sync ( ) ;
return AE_OK ;
}
static int acpi_processor_device_remove ( struct acpi_device * device )
{
pr_debug ( PREFIX " Xen does not support CPU hotremove \n " ) ;
return - ENOSYS ;
}
static void acpi_processor_hotplug_notify ( acpi_handle handle ,
u32 event , void * data )
{
struct acpi_processor * pr ;
struct acpi_device * device = NULL ;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE ; /* default */
int result ;
2013-02-17 11:47:24 +08:00
acpi_scan_lock_acquire ( ) ;
2013-01-25 15:43:34 +08:00
switch ( event ) {
case ACPI_NOTIFY_BUS_CHECK :
case ACPI_NOTIFY_DEVICE_CHECK :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Processor driver received %s event \n " ,
( event = = ACPI_NOTIFY_BUS_CHECK ) ?
" ACPI_NOTIFY_BUS_CHECK " : " ACPI_NOTIFY_DEVICE_CHECK " ) ) ;
if ( ! is_processor_present ( handle ) )
break ;
if ( ! acpi_bus_get_device ( handle , & device ) )
break ;
2013-02-17 11:47:24 +08:00
result = acpi_bus_scan ( handle ) ;
2013-01-25 15:43:34 +08:00
if ( result ) {
pr_err ( PREFIX " Unable to add the device \n " ) ;
break ;
}
2013-02-17 11:47:24 +08:00
result = acpi_bus_get_device ( handle , & device ) ;
if ( result ) {
pr_err ( PREFIX " Missing device object \n " ) ;
break ;
}
2013-01-25 15:43:34 +08:00
ost_code = ACPI_OST_SC_SUCCESS ;
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" received ACPI_NOTIFY_EJECT_REQUEST \n " ) ) ;
if ( acpi_bus_get_device ( handle , & device ) ) {
pr_err ( PREFIX " Device don't exist, dropping EJECT \n " ) ;
break ;
}
pr = acpi_driver_data ( device ) ;
if ( ! pr ) {
pr_err ( PREFIX " Driver data is NULL, dropping EJECT \n " ) ;
break ;
}
/*
* TBD : implement acpi_processor_device_remove if Xen support
* CPU hotremove in the future .
*/
acpi_processor_device_remove ( device ) ;
break ;
default :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Unsupported event [0x%x] \n " , event ) ) ;
/* non-hotplug event; possibly handled by other handler */
2013-02-17 11:47:24 +08:00
goto out ;
2013-01-25 15:43:34 +08:00
}
( void ) acpi_evaluate_hotplug_ost ( handle , event , ost_code , NULL ) ;
2013-02-17 11:47:24 +08:00
out :
acpi_scan_lock_release ( ) ;
2013-01-25 15:43:34 +08:00
}
static acpi_status is_processor_device ( acpi_handle handle )
{
struct acpi_device_info * info ;
char * hid ;
acpi_status status ;
status = acpi_get_object_info ( handle , & info ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
if ( info - > type = = ACPI_TYPE_PROCESSOR ) {
kfree ( info ) ;
return AE_OK ; /* found a processor object */
}
if ( ! ( info - > valid & ACPI_VALID_HID ) ) {
kfree ( info ) ;
return AE_ERROR ;
}
hid = info - > hardware_id . string ;
if ( ( hid = = NULL ) | | strcmp ( hid , ACPI_PROCESSOR_DEVICE_HID ) ) {
kfree ( info ) ;
return AE_ERROR ;
}
kfree ( info ) ;
return AE_OK ; /* found a processor device object */
}
static acpi_status
processor_walk_namespace_cb ( acpi_handle handle ,
u32 lvl , void * context , void * * rv )
{
acpi_status status ;
int * action = context ;
status = is_processor_device ( handle ) ;
if ( ACPI_FAILURE ( status ) )
return AE_OK ; /* not a processor; continue to walk */
switch ( * action ) {
case INSTALL_NOTIFY_HANDLER :
acpi_install_notify_handler ( handle ,
ACPI_SYSTEM_NOTIFY ,
acpi_processor_hotplug_notify ,
NULL ) ;
break ;
case UNINSTALL_NOTIFY_HANDLER :
acpi_remove_notify_handler ( handle ,
ACPI_SYSTEM_NOTIFY ,
acpi_processor_hotplug_notify ) ;
break ;
default :
break ;
}
/* found a processor; skip walking underneath */
return AE_CTRL_DEPTH ;
}
static
void acpi_processor_install_hotplug_notify ( void )
{
int action = INSTALL_NOTIFY_HANDLER ;
acpi_walk_namespace ( ACPI_TYPE_ANY ,
ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX ,
processor_walk_namespace_cb , NULL , & action , NULL ) ;
}
static
void acpi_processor_uninstall_hotplug_notify ( void )
{
int action = UNINSTALL_NOTIFY_HANDLER ;
acpi_walk_namespace ( ACPI_TYPE_ANY ,
ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX ,
processor_walk_namespace_cb , NULL , & action , NULL ) ;
}
static const struct acpi_device_id processor_device_ids [ ] = {
{ ACPI_PROCESSOR_OBJECT_HID , 0 } ,
{ ACPI_PROCESSOR_DEVICE_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , processor_device_ids ) ;
static struct acpi_driver xen_acpi_processor_driver = {
. name = " processor " ,
. class = ACPI_PROCESSOR_CLASS ,
. ids = processor_device_ids ,
. ops = {
. add = xen_acpi_processor_add ,
. remove = xen_acpi_processor_remove ,
} ,
} ;
static int __init xen_acpi_processor_init ( void )
{
int result = 0 ;
if ( ! xen_initial_domain ( ) )
return - ENODEV ;
/* unregister the stub which only used to reserve driver space */
xen_stub_processor_exit ( ) ;
result = acpi_bus_register_driver ( & xen_acpi_processor_driver ) ;
if ( result < 0 ) {
xen_stub_processor_init ( ) ;
return result ;
}
acpi_processor_install_hotplug_notify ( ) ;
return 0 ;
}
static void __exit xen_acpi_processor_exit ( void )
{
if ( ! xen_initial_domain ( ) )
return ;
acpi_processor_uninstall_hotplug_notify ( ) ;
acpi_bus_unregister_driver ( & xen_acpi_processor_driver ) ;
/*
* stub reserve space again to prevent any chance of native
* driver loading .
*/
xen_stub_processor_init ( ) ;
return ;
}
module_init ( xen_acpi_processor_init ) ;
module_exit ( xen_acpi_processor_exit ) ;
ACPI_MODULE_NAME ( " xen-acpi-cpuhotplug " ) ;
MODULE_AUTHOR ( " Liu Jinsong <jinsong.liu@intel.com> " ) ;
MODULE_DESCRIPTION ( " Xen Hotplug CPU Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;